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);
}
}