Read "Computer Graphics Programming (Using OpenGL and C++)" 3 - Drawing Triangles

GLSL runs on the GPU, and debugging requires a module for catching and displaying GLSL errors.

Utils.h

#pragma once
#include "GL\glew.h"
#include <string>
class Utils
{
public:
    Utils();
    ~Utils();
    static void printShaderLog(GLuint shader);
    static void printProgramLog(int prog);
    static bool checkOpenGLError();
    static GLuint createShaderProgram();
};

Utils.cpp

#include "Utils.h"
#include <iostream>
#include <fstream>
#include <SOIL2/SOIL2.h>

Utils::Utils()
{}

Utils::~Utils()
{}

// 当GLSL编译失败时,显示OpenGL日志内容
void Utils::printShaderLog(GLuint shader)
{
    int len = 0;
    int chWritten = 0;
    char *log;
    glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &len); // 提供有关编译过的GLSL着色器的信息
    if (len > 0)
    {
        log = (char *)malloc(len);
        glGetShaderInfoLog(shader, len, &chWritten, log);
        std::cout << "Shader Info Log: " << log << std::endl;
        free(log);
    }
}

// 当GLSL链接失败时,显示OpenGL日志内容
void Utils::printProgramLog(int prog)
{
    int len = 0;
    int chWritten = 0;
    char *log;
    glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &len); // 提供有关编译过的程序的信息
    if (len > 0)
    {
        log = (char *)malloc(len);
        glGetProgramInfoLog(prog, len, &chWritten, log);
        std::cout << "Program Info Log: " << log << std::endl;
        free(log);
    }
}

// 检查OpenGL错误标志,即是否发生OpenGL错误,既用于检测GLSL编译错误,又用于检测OpenGL运行时的错误
bool Utils::checkOpenGLError()
{
    bool foundError = false;
    int glErr = glGetError();
    while (glErr != GL_NO_ERROR)
    {
        std::cout << "glError: " << glErr << std::endl;
        foundError = true;
        glErr = glGetError();
    }
    return foundError;
}

GLuint Utils::createShaderProgram()
{
    GLint vertCompiled;
    GLint fragCompiled;
    GLint linked;

    const char* vshaderSource =
        "#version 460 \n"
        "void main(void) \n"
        "{ gl_Position = vec4(0.0, 0.0, 0.0, 1.0); }";

    const char* fshaderSource =
        "#version 460 \n"
        "out vec4 color; \n"
        "void main(void) \n"
        "{ color = vec4(0.0, 0.0, 1.0, 1.0); }";

    GLuint vShader = glCreateShader(GL_VERTEX_SHADER);
    GLuint fShader = glCreateShader(GL_FRAGMENT_SHADER);

    glShaderSource(vShader, 1, &vshaderSource, NULL);
    glShaderSource(fShader, 1, &fshaderSource, NULL);

    // 捕获编译着色器时的错误
    glCompileShader(vShader);
    checkOpenGLError();
    glGetShaderiv(vShader, GL_COMPILE_STATUS, &vertCompiled);
    if (vertCompiled != 1)
    {
        std::cout << "vertex compilation failed" << std::endl;
        printShaderLog(vShader);
    }

    glCompileShader(fShader);
    checkOpenGLError();
    glGetShaderiv(fShader, GL_COMPILE_STATUS, &fragCompiled);
    if (fragCompiled != 1)
    {
        std::cout << "fragment compilation failed" << std::endl;
        printShaderLog(fShader);
    }

    GLuint vfProgram = glCreateProgram();

    // 捕获链接着色器时的错误
    glAttachShader(vfProgram, vShader);
    glAttachShader(vfProgram, fShader);
    glLinkProgram(vfProgram);
    checkOpenGLError();
    glGetProgramiv(vfProgram, GL_LINK_STATUS, &linked);
    if (linked != 1)
    {
        std::cout << "linking failed" << std::endl;
        printProgramLog(vfProgram);
    }
    return vfProgram;
}

As the program becomes more complex, storing the GLSL shader code in strings becomes undesirable and we should instead store the shader codes in files and read them in.

The vertex shader and fragment shader code are placed in "vertShader.glsl" and "fragShader.glsl" respectively.

Add the following code to Utils.h:

    static std::string readShaderSource(const char *filePath);
    static GLuint createShaderProgram(const char *vp, const char *fp);

Utils.cpp

// 用于读取着色器的代码
std::string Utils::readShaderSource(const char * filePath)
{
    std::string content;
    std::ifstream fileStream(filePath, std::ios::in);
    std::string line = "";
    while (!fileStream.eof())
    {
        getline(fileStream, line);
        content.append(line + "\n");
    }
    fileStream.close();
    return content;
}

// 读取着色器代码并构建渲染程序,该类很适合重载
GLuint Utils::createShaderProgram(const char * vp, const char * fp)
{
    GLint vertCompiled;
    GLint fragCompiled;
    GLint linked;

    std::string vertShaderStr = readShaderSource(vp);
    std::string fragShaderStr = readShaderSource(fp);

    const char * vertShaderSrc = vertShaderStr.c_str();
    const char * fragShaderSrc = fragShaderStr.c_str();

    GLuint vShader = glCreateShader(GL_VERTEX_SHADER);
    GLuint fShader = glCreateShader(GL_FRAGMENT_SHADER);

    glShaderSource(vShader, 1, &vertShaderSrc, NULL);
    glShaderSource(fShader, 1, &fragShaderSrc, NULL);

    glCompileShader(vShader);
    checkOpenGLError();
    glGetShaderiv(vShader, GL_COMPILE_STATUS, &vertCompiled);
    if (vertCompiled != 1)
    {
        std::cout << "vertex compilation failed" << std::endl;
        printShaderLog(vShader);
    }

    glCompileShader(fShader);
    checkOpenGLError();
    glGetShaderiv(fShader, GL_COMPILE_STATUS, &fragCompiled);
    if (fragCompiled != 1)
    {
        std::cout << "fragment compilation failed" << std::endl;
        printShaderLog(fShader);
    }

    GLuint vfProgram = glCreateProgram();
    glAttachShader(vfProgram, vShader);
    glAttachShader(vfProgram, fShader);
    glLinkProgram(vfProgram);
    checkOpenGLError();
    glGetProgramiv(vfProgram, GL_LINK_STATUS, &linked);
    if (linked != 1)
    {
        std::cout << "linking failed" << std::endl;
        printProgramLog(vfProgram);
    }
    return vfProgram;
}

The program that reads and generates a triangle from a file shader is as follows:

Vertex shader, vertShader.glsl

#version 460 
void main(void)
{ if (gl_VertexID == 0) gl_Position = vec4(0.25, -0.25, 0.0, 1.0); 
else if (gl_VertexID == 1) gl_Position = vec4(-0.25, -0.25, 0.0, 1.0);
else gl_Position = vec4(0.25, 0.25, 0.0, 1.0);}

Add code to the display() function

...
glDrawArrays(GL_TRIANGLES, 0, 3); 

The result is as follows:

 The triangle can be moved to achieve animation effects. We only call init() once in the main() function, and then call display() repeatedly. It can be drawn again and again in the loop, as long as the display() function is designed to change the drawing over time. s things.

Each drawing of the scene is called a frame, and the frequency of calling display() is called the frame rate. The rate of movement within the program logic can be controlled by the elapsed time since the previous frame.

The variable "x" in the display() method is used to offset the X-axis position of the triangle. The value is different every frame, and when it reaches 1.0 or -1.0, it changes direction. The value in x is copied into the vertex shader's "offset" variable. The mechanism for performing this copying is called a uniform variable. The vertex shader adds offset to the X coordinate of the triangle being drawn. The background will be cleared each time display() is called to avoid leaving a trail of trails when the triangle moves.

main.cpp

// #include和定义与之前相同

float x = 0.0f;   // 三角形在x轴的位置
float inc = 1.0f; // 移动三角形的偏移量

void display(GLFWwindow* window, double currentTime)
{
    glClear(GL_DEPTH_BUFFER_BIT);
    glClearColor(0.0, 0.0, 0.0, 1.0);
    glClear(GL_COLOR_BUFFER_BIT);        // 每次将背景清除为黑色

    glUseProgram(renderingProgram);

    x += inc;                            // 切换至让三角形向右移动
    if (x > 1.0f)                        // 沿x轴移动三角形
        inc = -0.01f;
    if (x < -1.0f)                       // 切换至让三角形向右移动
        inc = 0.01f;
    GLuint offsetLoc = glGetUniformLocation(renderingProgram, "offset"); // 获取"offset"指针
    glProgramUniform1f(renderingProgram, offsetLoc, x);                  // 将"x"中的值传给"offset"
    glDrawArrays(GL_TRIANGLES, 0, 3);
}
// 其余函数同前

vertex shader

#version 460 
uniform float offset;
void main(void)
{ if (gl_VertexID == 0) gl_Position = vec4(0.25 + offset, -0.25, 0.0, 1.0); 
  else if (gl_VertexID == 1) gl_Position = vec4(-0.25 + offset, -0.25, 0.0, 1.0);
  else gl_Position = vec4(0.25 + offset, 0.25, 0.0, 1.0);}

The result is as follows:

Guess you like

Origin blog.csdn.net/ttod/article/details/135343331