shader 着色器
着色器是運作在GPU上面的小程式,這些小程式為圖形渲染的某個特定部份而運作,從基本意義上來說,着色器隻一種把輸入轉換為輸出的程式。
着色器的開頭聲明版本,接着輸入和輸出變量,uniform和main函數。
一個典型的着色器有下面的結構
#version version_number
in type in_variable_name;
in type in_variable_name;
out type out_variable_name;
uniform type uniform_name;
int main()
{
// 處理輸入并進行一些圖形操作
...
// 輸出處理過的結果到輸出變量
out_variable_name = weird_stuff_we_processed;
}
頂點着色器的輸入變量也叫頂點屬性(Vertex Attribute),它由硬體來決定,OpenGL確定至少有16個包含4分量的頂點屬性可以用,可以用GL_MAX_VERTEX_ATTRIBS來查詢
GLint nrAttributes;
glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &nrAttributes);
std::cout << "Maximum nr of vertex attributes supported: " << nrAttributes << std::endl;
資料類型
GLSL中包含C等其它語言大部份的預設的資料類型,如
int
,
float
,
double
,
uint
,
bool
.GLSL也有兩種容器類型,矩陣(Matrix),向量(Vector)
向量
類型 | 說明 |
---|---|
vecn | 包含n個float分量的的預設向量 |
bvecn | 包含n個bool分量的向量 |
ivecn | 包含n個int分量的向量 |
uvecn | 包含n個unsigned int 分量的向量 |
dvecn | 包含n個double分量的向量 |
輸入與輸出
頂點着色器接收的是一種特殊行式的輸入,頂點着色器的輸入特殊在,它從頂點資料中直接接收輸入。為了定義頂點資料該如何管理,我們使用location這一進制資料指定輸入變量,這樣我們才可以在CPU上配置頂點屬性。我們已經在前面的教程看過這個了,layout (location = 0)。頂點着色器需要為它的輸入提供一個額外的layout辨別,這樣我們才能把它連結到頂點資料。
另一個例外是片段着色器,它需要一個vec4顔色輸出變量,因為片段着色器需要生成一個最終輸出的顔色。如果你在片段着色器沒有定義輸出顔色,OpenGL會把你的物體渲染為黑色(或白色)。
是以,如果我們打算從一個着色器向另一個着色器發送資料,我們必須在發送方着色器中聲明一個輸出,在接收方着色器中聲明一個類似的輸入。當類型和名字都一樣的時候,OpenGL就會把兩個變量連結到一起,它們之間就能發送資料了(這是在連結程式對象時完成的)。
//
// main.cpp
// shaders0
//
// Created by xufan on 2017/5/16.
// Copyright © 2017年 xufan. All rights reserved.
//
#include <iostream>
#include <math.h>
#define GLEW_STATIC
#include <GL/glew.h>
#include <GLFW/glfw3.h>
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode);
void display();
// Shaders
const GLchar *vertexShaderSource = " \n \
#version 330 core \n \
layout (location = 0) in vec3 position; // position變量的屬性位置值為0 \n \
layout (location = 1) in vec3 color; // 顔色變量的屬性位置值為1 \n \
\n \
out vec3 ourColor; // 為片段着色器輸出一個顔色輸出\n \
\n \
void main() \n \
{ \n \
gl_Position = vec4(position, 1.0); // 注意我們如何把一個vec3作為vec4的構造器的參數\n \
ourColor = color; //将ourColor設定為我們從頂點資料那裡得到的輸入顔色\n \
}\n";
const GLchar* fragmentShaderSource = "\n \
#version 330 core \n \
in vec3 ourColor; // 從頂點着色器傳來的輸入變量(名稱相同、類型相同)\n \
\n \
out vec4 color; // 片段着色器輸出的變量名可以任意命名,類型必須是vec4\n \
//uniform vec4 ourColor; //在OpenGL程式代碼中設定這個變量\n \
\n \
void main() \n \
{ \n \
color = vec4(ourColor, 1.0f); \n \
//color = ourColor; \n \
}\n";
int main()
{
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, );
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, );
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
GLFWwindow *window = glfwCreateWindow(, , "learnOpenGL", nullptr, nullptr);
glfwMakeContextCurrent(window);
glfwSetKeyCallback(window, key_callback);
glewExperimental = GL_TRUE;
glewInit();
printf("%s\n",vertexShaderSource);
printf("%s\n",fragmentShaderSource);
GLint nrAttributes;
glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &nrAttributes);
std::cout << "Maximum nr of vertex attributes supported: " << nrAttributes << std::endl;
int width,height;
glfwGetFramebufferSize(window, &width, &height);
glViewport(, , width, height);
//vertex shader
GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, , &vertexShaderSource, NULL);
glCompileShader(vertexShader);
//check
GLint success;
GLchar infoLog[];
glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
if (!success) {
glGetShaderInfoLog(vertexShader, , NULL, infoLog);
std::cout<<"vertex shader compilation failed"<<std::endl;
}
//fragmeng shader
GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, , &fragmentShaderSource,NULL);
glCompileShader(fragmentShader);
glGetShaderiv(fragmentShader,GL_COMPILE_STATUS, &success);
if (!success) {
glGetShaderInfoLog(fragmentShader, , NULL, infoLog);
std::cout<<"fragment shader compilation failed"<<std::endl;
}
//create a program
GLuint shaderProgram = glCreateProgram();
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);
glLinkProgram(shaderProgram);
//check
glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);
if (!success) {
glGetProgramInfoLog(shaderProgram, , NULL, infoLog);
std::cout<<"shaderPorgram failed"<<std::endl;
}
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
// GLfloat vertices[] = {
// -0.5f, -0.5f, 0.0f, // Left
// 0.5f, -0.5f, 0.0f, // Right
// 0.0f, 0.5f, 0.0f // Top
// };
// GLfloat vertices[] = {
//
//
//
// // 第一個三角形
// 0.5f, 0.5f, 0.0f, // 右上角
// 0.5f, -0.5f, 0.0f, // 右下角
// -0.5f, 0.5f, 0.0f, // 左上角
// // 第二個三角形
// 0.5f, -0.5f, 0.0f, // 右下角
// -0.5f, -0.5f, 0.0f, // 左下角
// -0.5f, 0.5f, 0.0f // 左上角
//
// };
GLfloat vertices[] = {
// 位置 // 顔色
, -, , , , , // 右下
-, -, , , , , // 左下
, , , , , // 頂部
};
GLuint VBO; // vertex buffer object,頂點緩沖對象
GLuint VAO; // vertex array object ,頂點數組對象
glGenVertexArrays(, &VAO);
glGenBuffers(, &VBO);
//綁定
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
//複制
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
//設定頂點屬性指針,位置屬性
glVertexAttribPointer(, , GL_FLOAT, GL_FALSE, sizeof(GL_FLOAT) * , (GLvoid*)());
//啟動頂點屬性,位置是0
glEnableVertexAttribArray();
//設定顔色屬性,啟動
glVertexAttribPointer(, , GL_FLOAT, GL_FALSE, sizeof(GLfloat) * , (GLvoid *)( * sizeof(GLfloat)));
glEnableVertexAttribArray();
//解綁
glBindBuffer(GL_ARRAY_BUFFER, );
glBindVertexArray();
//線框
//glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
//GL_FILL 填充
while (!glfwWindowShouldClose(window)) {
glfwPollEvents();
display();
//激活着色器
glUseProgram(shaderProgram);
// GLfloat timeValue = glfwGetTime();
// GLfloat greenValue = (sin(timeValue) / 2) + 0.5;
// GLint vertextColorLocation = glGetUniformLocation(shaderProgram, "ourColor");
// glUniform4f(vertextColorLocation, 0.0f, greenValue, 0.0f, 1.0f);
//
//繪制這個三角形
glBindVertexArray(VAO);
glDrawArrays(GL_TRIANGLES, , sizeof(vertices)/ ( * sizeof(vertices[])));
glBindVertexArray();
glfwSwapBuffers(window);
}
glDeleteVertexArrays(,&VAO);
glDeleteBuffers(, &VBO);
glfwTerminate();
return ;
}
void display()
{
//緩沖顔色
glClearColor(, , , );
//清空顔色緩沖
glClear(GL_COLOR_BUFFER_BIT);
}
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode)
{
if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) {
std::cout<<"enter escape";
glfwSetWindowShouldClose(window, GL_TRUE);
}
}