zoukankan      html  css  js  c++  java
  • OpenGL编程逐步深入(五)Uniform 变量

    准备知识

    在这个教程中我们会遇到一种新的Shader变量类型,即uniform变量。attribute(属性)变量和uniform变量的不同之处在于attribute 变量中包含顶点的具体数据,当每次执行shader调用时会从顶点缓存中重新加载一个新的值。而uniform类型的变量在整个绘制调用中始终使用同一个变量。这意味着你在绘制调用前加载的值在每个vertex shader调用时能访问到相同的值。uniform变量在存储光照参数(光照位置、方向等)、变换矩阵、纹理对象句柄等这一类型的数据时非常有用。

    在这个教程中我们最终会看到一些东西在屏幕上移动。我们將它和一个uniform变量进行绑定,GLUT为我们提供了空闲回调函数,在绘制每一帧的时候改变变量的值。GLUT并不会重复的调用渲染回调函数,除非在需要的时候,例如窗口最大化、最小化或被其他窗口覆盖。如果我们在启动应用程序后不对窗口进行任何操作,渲染回调函数仅仅会被调用一次。可以通过在这个回调函数中使用printf向控制台打印信息来验证这个结论。在GLUT中只注册渲染回调函数在前几节教程中没什么问题,但本节我们需要反复的改变变量的值,因此我们通过注册空闲回调函数实现。空闲处理函数即使没有接收到来自Windows系统事件时也会被GLUT实时的调用。你可以为这个回调专门写一个函数,或者把渲染回调函数也指定为空闲回调函数。在本教程我们我们采用后者,在渲染回调函数中更新变量的值。

    项目配置

    参考上节。

    程序代码

    我们在上一节代码基础上进行调整。

    清单1.主程序 tutorial05.cpp代码

    /*
    
        Copyright 2010 Etay Meiri
    
        This program is free software: you can redistribute it and/or modify
        it under the terms of the GNU General Public License as published by
        the Free Software Foundation, either version 3 of the License, or
        (at your option) any later version.
    
        This program is distributed in the hope that it will be useful,
        but WITHOUT ANY WARRANTY; without even the implied warranty of
        MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
        GNU General Public License for more details.
    
        You should have received a copy of the GNU General Public License
        along with this program.  If not, see <http://www.gnu.org/licenses/>.
    
        Tutorial 05 - uniform variables
    */
    
    #include "stdafx.h"
    #include <stdio.h>
    #include <string.h>
    #include <assert.h>
    #include <math.h>
    #include <GL/glew.h>
    #include <GL/freeglut.h>
    #include "ogldev_math_3d.h"
    GLuint VBO;
    GLuint gScaleLocation;
    
    const char* pVSFileName = "shader.vs";
    const char* pFSFileName = "shader.fs";
    
    static void RenderSceneCB()
    {
        glClear(GL_COLOR_BUFFER_BIT);
        static float Scale = 0.0f;
        Scale += 0.001f;
        glUniform1f(gScaleLocation, sinf(Scale));
        glEnableVertexAttribArray(0);
        glBindBuffer(GL_ARRAY_BUFFER, VBO);
        glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
        glDrawArrays(GL_TRIANGLES, 0, 3);
        glDisableVertexAttribArray(0);
        glutSwapBuffers();
    }
    
    static void InitializeGlutCallbacks()
    {
        glutDisplayFunc(RenderSceneCB);
        glutIdleFunc(RenderSceneCB);
    }
    
    static void CreateVertexBuffer()
    {
        Vector3f Vertices[3];
        Vertices[0] = Vector3f(-1.0f, -1.0f, 0.0f);
        Vertices[1] = Vector3f(1.0f, -1.0f, 0.0f);
        Vertices[2] = Vector3f(0.0f, 1.0f, 0.0f);
    
        glGenBuffers(1, &VBO);
        glBindBuffer(GL_ARRAY_BUFFER, VBO);
        glBufferData(GL_ARRAY_BUFFER, sizeof(Vertices), Vertices, GL_STATIC_DRAW);
    }
    
    static void AddShader(GLuint ShaderProgram, const char* pShaderText, GLenum ShaderType)
    {
        GLuint ShaderObj = glCreateShader(ShaderType);
        if (ShaderObj == 0) {
            fprintf(stderr, "Error creating shader type %d
    ", ShaderType);
            exit(1);
        }
        const GLchar* p[1];
        p[0] = pShaderText;
        GLint Lengths[1];
        Lengths[0]= strlen(pShaderText);
        glShaderSource(ShaderObj, 1, p, Lengths);
        glCompileShader(ShaderObj);
        GLint success;
        glGetShaderiv(ShaderObj, GL_COMPILE_STATUS, &success);
        if (!success) {
            GLchar InfoLog[1024];
            glGetShaderInfoLog(ShaderObj, 1024, NULL, InfoLog);
            fprintf(stderr, "Error compiling shader type %d: '%s'
    ", ShaderType, InfoLog);
            exit(1);
        }
        glAttachShader(ShaderProgram, ShaderObj);
    }
    
    static void CompileShaders()
    {
        GLuint ShaderProgram = glCreateProgram();
        if (ShaderProgram == 0) {
            fprintf(stderr, "Error creating shader program
    ");
            exit(1);
        }
        string vs, fs;
        if (!ReadFile(pVSFileName, vs)) {
            exit(1);
        };
        if (!ReadFile(pFSFileName, fs)) {
            exit(1);
        };
        AddShader(ShaderProgram, vs.c_str(), GL_VERTEX_SHADER);
        AddShader(ShaderProgram, fs.c_str(), GL_FRAGMENT_SHADER);
        GLint Success = 0;
        GLchar ErrorLog[1024] = { 0 };
        glLinkProgram(ShaderProgram);
        glGetProgramiv(ShaderProgram, GL_LINK_STATUS, &Success);
        if (Success == 0) {
            glGetProgramInfoLog(ShaderProgram, sizeof(ErrorLog), NULL, ErrorLog);
            fprintf(stderr, "Error linking shader program: '%s'
    ", ErrorLog);
            exit(1);
        }
        glValidateProgram(ShaderProgram);
        glGetProgramiv(ShaderProgram, GL_VALIDATE_STATUS, &Success);
        if (!Success) {
            glGetProgramInfoLog(ShaderProgram, sizeof(ErrorLog), NULL, ErrorLog);
            fprintf(stderr, "Invalid shader program: '%s'
    ", ErrorLog);
            exit(1);
        }
        glUseProgram(ShaderProgram);
        gScaleLocation = glGetUniformLocation(ShaderProgram, "gScale");
        assert(gScaleLocation != 0xFFFFFFFF);
    }
    int _tmain(int argc, _TCHAR* argv[])
    {
        glutInit(&argc, argv);
        glutInitDisplayMode(GLUT_DOUBLE|GLUT_RGBA);
        glutInitWindowSize(1024, 768);
        glutInitWindowPosition(100, 100);
        glutCreateWindow("Tutorial 05");
        InitializeGlutCallbacks();
        // Must be done after glut is initialized!
        GLenum res = glewInit();
        if (res != GLEW_OK) {
            fprintf(stderr, "Error: '%s'
    ", glewGetErrorString(res));
            return 1;
        }
        printf("GL version: %s
    ", glGetString(GL_VERSION));
        glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
        CreateVertexBuffer();
        CompileShaders();
        glutMainLoop();
        return 0;
    }

    代码解读

    glutIdleFunc(RenderSceneCB);

    这里我们將渲染回调函数RenderSceneCB也注册为空闲回调函数,要注意的是如果你你打算定义一个专门的函数处理空闲回调,需要在后面增加glutPostRedisplay() 函数的调用,这样的话空闲回调函数就会反复的调用而渲染回调函数则不会。

    gScaleLocation = glGetUniformLocation(ShaderProgram, "gScale");
    assert(gScaleLocation != 0xFFFFFFFF);

    在链接后,我们查询Program对象获取uniform变量的位置,这是另外一个c/c++执行环境需要映射到shader执行环境的案例。你不能直接访问shader的内容,也不能直接更新shader的变量。当你编译shader后,GLSL编译器会为每一个uniform变量分配一个索引。shader在编译器内部表示中都通过索引来访问它的变量。这个索引在程序中通过glGetUniformLocation也能够获取,调用函数时需要传入program对象的句柄和变量名称,该函数会返回变量的索引(出错时返回-1)。这里检测错误是非常必要的,否则将来更新变量时不会传递到shader中。这个函数调用失败主要两个原因,你的变量名拼写错误或它被编译器优化掉了,如果GLSL编译器发现该变量在Shader中没有用到就会去除它,这种情况下会导致glGetUniformLocation 调用失败。

    static float Scale = 0.0f;
    Scale += 0.001f;
    glUniform1f(gScaleLocation, sinf(Scale));

    这里我们定义一个静态float类型变量Scale ,在每次调用渲染回调函数时把它的值增加0.001,实际上传递给shader的值是Scale 变量的正弦值,这样做很好的创建了一个在-1.0到1.0之间的循环。需要注意的是sinf的参数是要传入一个弧度还是角度,这里我们无需关注,我们只需要产生一个在-1到1的正弦波。sinf函数的返回值通过glUniform1f函数传递到shader中,OpenGl提供的这个函数有多种形式glUniform{1234}{if}。你可以使用它將值加载到1维、2维、3维或4维向量中。第一个参数为之前调用glGetUniformLocation获取的位置索引。

    清单2. shader.vs代码

    #version 330
    
    layout (location = 0) in vec3 Position;
    
    uniform float gScale;
    
    void main()
    {
        gl_Position = vec4(gScale * Position.x, gScale * Position.y, Position.z, 1.0);
    }
    
    uniform float gScale;

    这里我们定义了一个uniform类型变量。

    gl_Position = vec4(gScale * Position.x, gScale * Position.y, Position.z, 1.0);

    我们用gScale乘上Positon向量X/Y的值,确保vec4函数参数值在每一帧渲染时会改变,然后你就能够解释屏幕中的三角形为什么一会变大一会变小。

    编译运行

    运行程序你会看到大小不断变化的三角形。

    这里写图片描述
    这里写图片描述

  • 相关阅读:
    大数据方向招人难啊!!
    .netcore 急速接入第三方登录,不看后悔
    T-SQL——函数——时间操作函数
    T-SQL——关于XML类型
    机器学习方法
    可读性友好的JavaScript:两个专家的故事
    快速了解 JavaScript ES2019 的五个新增特性
    了解 Vue 的 Compsition API
    使用 JavaScript 操作浏览器历史记录 API
    JavaScript ES 模块:现代化前端编程必备技能
  • 原文地址:https://www.cnblogs.com/lanzhi/p/6468986.html
Copyright © 2011-2022 走看看