OpenGL基础6:着色器


如果想要完整的代码,可以用上一章最下面那份代码,然后进行局部替换就OK了

一、着色器结构

一个简单的着色器如下:

#version 3.4       //版本号

in type name1    //输入数据
out type name2   //输出数据
uniform type name3       //全局属性

int main()
{
  // 处理输入并进行一些图形操作
  ...
  // 输出处理过的结果到输出变量
  name2 = xxx;
}

其中对于顶点着色器,输入数据叫做顶点属性(Vertex Attribute),也就是颜色位置这些,一般来讲顶点属性能声明的上限为16个,有些硬件可能更高,可以通过以下代码查询:(注意要在初始化之后)

GLint nrAttributes;
glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &nrAttributes);
std::cout << "Maximum nr of vertex attributes supported: " << nrAttributes << std::endl;

两个着色器数据的连接:

如果我们打算从一个着色器向另一个着色器发送数据,我们只需要在当前着色器中声明一个输出,在下一个着色器中声明一个同类型同名字的输入,OpenGL就会将这两个变量链接到一起,例如我们可以来个比较奇葩的操作:让顶点着色器为片段着色器决定颜色,如下:

// Shaders
const GLchar* VShader =
"#version 330 core\n"
"layout (location = 0) in vec3 position;\n"
"out vec4 colorIn;\n"
"void main()\n"
"{\n"
    "gl_Position = vec4(position.x, position.y, position.z, 1.0);\n"
    "colorIn = vec4(0.2f, 0.3f, 0.4f, 1);\n"
"}\n\0";

const GLchar* FShaderY =
"#version 330 core\n"
"in vec4 colorIn;\n"
"out vec4 colorOut;\n"
"void main()\n"
"{\n"
    "colorOut = colorIn;\n"
"}\n\0";

对于片段着色器,我们必须要一个vec4的向量输出作为最终颜色,如果没有就会默认全黑或者全白,注意如果你的GLSL编译失败,也会出现全黑或者全白的情况

对于顶点着色器,它从顶点数据中直接接收输入,我们需要使用location这一元数据指定输入变量,也就是使用 "layout (location = 0)" 设置位置值,这样我们才可以在CPU上配置顶点属性

 

二、向量数据类型

GLSL中几种常见向量如下,其中n≤4

  • vecn:包含n个float分量的默认向量
  • bvecn:包含n个bool分量的向量
  • ivecn:包含n个int分量的向量
  • uvecn:包含n个unsigned int分量的向量
  • dvecn:包含n个double分量的向量

我们可以用xyzw表示其所有分量,对于颜色来讲,我们也可以用rgba

向量可以随意组合,这种趣而灵活的分量选择方式,叫做重组(Swizzling),如下:

vec2 someVec;
vec4 differentVec = someVec.xyxx;
vec3 anotherVec = differentVec.zyw;
vec4 otherVec = someVec.xxxx + anotherVec.yxzy;
vec2 vect = vec2(0.5f, 0.7f);
vec4 result = vec4(vect, 0.0f, 0.0f);
vec4 otherResult = vec4(result.xyz, 1.0f);

当然要遵循基本原则,例如参数要对的上,不能对类似于vec2之类的向量访问其zw属性等

 

三、全局统一变量 Uniform

Uniform是一种从CPU中的应用向GPU中的着色器发送数据的方式,也是GLSLshader中的全局变量,可以在任意的shader中访问,这也意味着uniform变量必须在每个着色器程序对象中都是独一无二的

如下,我们在片段着色器中声明了一个uniform变量,并把片段着色器的输出颜色设置为uniform值的内容。因为uniform是全局变量,我们可以在任何着色器中定义它们,而无需通过顶点着色器作为中介

#version 330 core
out vec4 color;
uniform vec4 ourColor;     // 在OpenGL程序代码中设定这个变量
void main()
{
    color = ourColor;
}  

注意:如果你声明了一个uniform却在GLSL代码中没用过,编译器会静默移除这个变量,最后编译出的版本中就并不会包含它

接下来就是为uniform添加数据了,一个例子:

glUseProgram(shaderYellow);
GLfloat timeValue = glfwGetTime();
GLfloat greenValue = (sin(timeValue) / 2) + 0.5;
GLint vertexColorLocation = glGetUniformLocation(shaderYellow, "colorOut");
glUniform4f(vertexColorLocation, greenValue, greenValue, 0.0f, 1.0f);
  • glUseProgram(shaderYellow):激活着色器
  • glfwGetTime():获取当前运行的时间
  • glGetUniformLocation(shaderYellow, "colorOut"):获取当前uniform变量colorOut的位置值,如果返回-1表示没有找到
  • glUniform4f(vertexColorLocation, greenValue, greenValue, 0.0f, 1.0f):设置uniform值,其中第一个参数为位置值

注意上面的函数名glUniform4f,后面的4f就表示需要传入4个float,因为C不支持重载,所以只要参数不同就必须要一个新的函数,如下:你可以找到好多个一样前缀的方法

完整代码如下:

#pragma comment(lib,"glew32.lib")
#include<iostream>
#include<opengl/glew.h>
#define GLEW_STATIC
#include<GLFW/glfw3.h>
#include<opengl/freeglut.h>

void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode);
const GLuint WIDTH = 800, HEIGHT = 600;

const GLchar* VShader =
"#version 330 core\n"
"layout (location = 0) in vec3 position;\n"
"void main()\n"
"{\n"
    "gl_Position = vec4(position, 1.0);\n"              //这里就用到了上面所说的“重组”
"}\n\0";

const GLchar* FShaderY =
"#version 330 core\n"
"out vec4 color;\n"
"uniform vec4 colorOut;\n"
"void main()\n"
"{\n"
    "color = colorOut;\n"
"}\n\0";

const GLchar* FShaderB =
"#version 330 core\n"
"out vec4 color;\n"
"void main()\n"
"{\n"
    "color = vec4(0.1f, 0.1f, 1.0f, 1.0f);\n"
"}\n\0";

int main()
{
    glfwInit();
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);

    GLFWwindow* window = glfwCreateWindow(WIDTH, HEIGHT, "LearnOpenGL", nullptr, nullptr);
    glfwMakeContextCurrent(window);
    glfwSetKeyCallback(window, key_callback);
    glewExperimental = GL_TRUE;
    glewInit();

    int width, height;
    glfwGetFramebufferSize(window, &width, &height);
    glViewport(0, 0, width, height);

    GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(vertexShader, 1, &VShader, NULL);
    glCompileShader(vertexShader);
    GLuint fShaderY = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(fShaderY, 1, &FShaderY, NULL);
    glCompileShader(fShaderY);
    GLuint fShaderB = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(fShaderB, 1, &FShaderB, NULL);
    glCompileShader(fShaderB);

    GLuint shaderYellow = glCreateProgram();
    GLuint shaderBlue = glCreateProgram();
    glAttachShader(shaderYellow, vertexShader);
    glAttachShader(shaderYellow, fShaderY);
    glLinkProgram(shaderYellow);
    glAttachShader(shaderBlue, vertexShader);
    glAttachShader(shaderBlue, fShaderB);
    glLinkProgram(shaderBlue);

    glDeleteShader(vertexShader);
    glDeleteShader(fShaderY);
    glDeleteShader(fShaderB);

    GLfloat trangleY[] =
    {
        -0.5f, -0.5f, 0.0f,
        -0.5f, 0.5f, 0.0f,
        0.0f, 0.0f, 0.0f
    };
    GLfloat trangleB[] =
    {
        0.5f, -0.5f, 0.0f,
        0.5f, 0.5f, 0.0f,
        0.0f, 0.0f, 0.0f
    };
    GLuint VBO[2], VAO[2];
    glGenVertexArrays(2, VAO);
    glGenBuffers(2, VBO);

    glBindVertexArray(VAO[0]);
    glBindBuffer(GL_ARRAY_BUFFER, VBO[0]);
    glBufferData(GL_ARRAY_BUFFER, sizeof(trangleY), trangleY, GL_STATIC_DRAW);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (GLvoid*)0);
    glEnableVertexAttribArray(0);

    glBindVertexArray(VAO[1]);
    glBindBuffer(GL_ARRAY_BUFFER, VBO[1]);
    glBufferData(GL_ARRAY_BUFFER, sizeof(trangleB), trangleB, GL_STATIC_DRAW);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (GLvoid*)0);
    glEnableVertexAttribArray(0);

    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glBindVertexArray(0);

    while (!glfwWindowShouldClose(window))
    {
        glfwPollEvents();
        glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);

        glUseProgram(shaderYellow);
        GLfloat timeValue = glfwGetTime();
        GLfloat greenValue = (sin(timeValue) / 2) + 0.5;
        GLint vertexColorLocation = glGetUniformLocation(shaderYellow, "colorOut");
        //std::cout << vertexColorLocation << std::endl;
        glUniform4f(vertexColorLocation, greenValue, greenValue, 0.0f, 1.0f);


        glUseProgram(shaderYellow);
        glBindVertexArray(VAO[0]);
        glDrawArrays(GL_TRIANGLES, 0, 3);
        glUseProgram(shaderBlue);
        glBindVertexArray(VAO[1]);
        glDrawArrays(GL_TRIANGLES, 0, 3);

        glBindVertexArray(0);
        glfwSwapBuffers(window);
    }
    glDeleteVertexArrays(2, VAO);
    glDeleteBuffers(2, VBO);
    glfwTerminate();
    return 0;
}

void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode)
{
    if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
        glfwSetWindowShouldClose(window, GL_TRUE);
}

运行后可以看到左边的三角形颜色会随时间变化

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值