Opengl – 图形学 – C/C++ 每天进步一点点【丑牛】

前言

上一回合讲到 C/C++ 开发环境的搭建,基础搞好了,这里终于可以进入正题了,废话不多话,先找 gpt 帮忙开个头,it goes like this:)
PROMPT:介绍一下GLFW和GLAD
ANSWER:GLFW和GLAD是两个常用的库,用于在现代OpenGL应用程序中进行窗口管理和加载OpenGL函数。
1、 GLFW(OpenGL Framework)是一个轻量级的开源库,用于创建和管理窗口、处理用户输入(如键盘、鼠标、游戏手柄等),以及获取时间间隔。它提供了一个简单的接口,方便开发者快速搭建OpenGL应用程序的窗口环境。GLFW跨平台,可以在Windows、MacOS和Linux等操作系统上使用。
2、 GLAD(OpenGL Loading Library)是一个开源的库,用于加载OpenGL函数。在早期版本的OpenGL中,需要手动查找和加载函数指针才能使用OpenGL的功能。而GLAD简化了这个过程,它自动生成了跨平台的OpenGL加载器代码,可以自动获取系统支持的OpenGL版本和扩展,并将相关函数指针加载到程序中,方便开发者使用OpenGL的各种功能。
glfw和glad常常被一起使用。开发者可以使用GLFW创建窗口、处理用户输入,然后使用GLAD加载特定OpenGL版本的函数,并在GLFW创建的窗口环境中使用这些函数实现图形渲染以及其他OpenGL相关的操作。这样,开发者能够更方便地进行现代OpenGL应用程序的开发。【感谢 chatgpt 3.5 帮忙 :)】,介绍得不错,确实就这么回事,之前还有个 GLU(OpenGL Utility Library),但相对来说较少使用,因为现代OpenGL更倾向于使用自定义的解决方案来实现类似的功能。
BTW, 还有个 GLEW
PROMPT:glew 与 glad 有什么不一样?
ANSWER
GLEW(OpenGL Extension Wrangler Library)和GLAD(OpenGL Loader)是两个用于加载和管理OpenGL扩展函数的库,它们有以下不同之处:
1. 历史和使用范围:GLEW是比较早期的OpenGL扩展加载库,已经存在很长时间,并广泛用于许多项目。而GLAD是近年来出现的一个轻量级OpenGL扩展加载库,在性能和易用性方面进行了优化。
2. 生成方式:GLEW是使用C库的方式提供的,你需要下载和编译GLEW的源代码,然后将其连接到你的项目中。而GLAD是一个在线生成器,你可以使用它的在线服务来生成适用于你的OpenGL版本和扩展的自定义加载代码。
3. 用法和初始化:GLEW的用法相对来说更简单,只需要在初始化OpenGL上下文之后,调用`glewInit()`函数来初始化GLEW,并加载和绑定OpenGL的扩展函数。GLAD的使用方式也相对简单,你需要调用`gladLoadGL()`函数来加载OpenGL函数指针,并且确保在初始化OpenGL上下文后调用该函数。
4. 支持的OpenGL版本:GLEW支持的OpenGL版本比较广泛,并且提供了很好的向后兼容性,可以加载旧版本和最新版本的扩展函数。GLAD的生成器允许你选择特定的OpenGL版本和扩展,生成适用于你的需求的加载代码。

需要注意的是,无论是GLEW还是GLAD,它们的目的都是加载和管理OpenGL的扩展函数。你可以根据自己的需求选择其中之一来使用,它们在实际使用中并没有明显的性能优劣之分。选择合适的库主要取决于你对功能、使用习惯和项目要求的考虑。

画第一个三角形 Helloworld Triangle

开始之前,还是先把 GLFW 和 GLAD 给弄下来。
GLFW 搞起
https://www.glfw.org/download.html 我直接下载 64-bit Windows binaries,我偷懒直接使用(LearnOpengl.com 建议下源码自己构建,自己构建应该也比较简单,直接用 cmake build 就行了,后面有些例子如用于加载模形的 Assimp 万能库就必须要自己 build 了,因为可能与本地环境相关,比如 windows 支持 windows 特有 d3d等)。
GLAD 搞起
这个牛,直接在线自己构建 https://glad.dav1d.de/
我版本直接选了最高,如下:

获取 GLAD
获取 GLAD

万事具备,只欠东风啦。
我们的第一个 Opengl Helloworld 呼之欲出
1、把上面准备的依赖引入到我们的项目当中

配置好glfw和glad依赖
配置好glfw和glad依赖

2、先看看源码

Hello Triangle
Hello Triangle

当然也可以直接去看源版:
https://learnopengl.com/code_viewer_gh.php?code=src/1.getting_started/2.2.hello_triangle_indexed/hello_triangle_indexed.cpp

源码分析:

#include <glad/glad.h>
#include <GLFW/glfw3.h>

#include <iostream>

// 相当于头文件,先定义了下面用到的函数
void framebuffer_size_callback(GLFWwindow *window, int width, int height); // 处理窗口大小变化事件
void processInput(GLFWwindow *window); // 处理键盘,鼠标等输入事件

// settings
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;

// 不太优雅哈哈,后面会用单独文件加载进来,vs 与 fs 两文件
const char *vertexShaderSource = "#version 330 core\n"
                                 "layout (location = 0) in vec3 aPos;\n"
                                 "void main()\n"
                                 "{\n"
                                 "   gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n"
                                 "}\0";
const char *fragmentShaderSource = "#version 330 core\n"
                                   "out vec4 FragColor;\n"
                                   "void main()\n"
                                   "{\n"
                                   "   FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);\n"
                                   "}\n\0";

int main()
{
    // glfw: initialize and configure
    // https://www.glfw.org/docs/latest/window.html#window_hints
    // ------------------------------
    glfwInit();
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); // 我们用的 OpenGL version 3.3
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); // 我们用的 OpenGL version 3.3
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // 定义OpenGL模式(profile),使用的是核心模式。( core)

// 夸平台处理
#ifdef __APPLE__
    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
#endif

    // glfw window creation, 创建窗口
    // --------------------
    GLFWwindow *window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);

    if (window == NULL)
    {
        std::cout << "Failed to create GLFW window" << std::endl;
        glfwTerminate();
        return -1;
    }

    glfwMakeContextCurrent(window);
    glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);

    // glad: load all OpenGL function pointers
    // ---------------------------------------
    if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
    {
        std::cout << "Failed to initialize GLAD" << std::endl;
        return -1;
    }
  
    // 下面跟 webgl 差不多玩法去 处理 shader
    // build and compile our shader program
    // ------------------------------------
    // vertex shader
    unsigned int vertexShader = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
    glCompileShader(vertexShader);

    // check for shader compile errors
    int success;
    char infoLog[512];
    glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);

    if (!success)
    {
        glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);
        std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n"
                  << infoLog << std::endl;
    }

    // fragment shader
    unsigned int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
    glCompileShader(fragmentShader);

    // check for shader compile errors
    glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);

    if (!success)
    {
        glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog);
        std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n"
                  << infoLog << std::endl;
    }

    // link shaders
    unsigned int shaderProgram = glCreateProgram();
    glAttachShader(shaderProgram, vertexShader);
    glAttachShader(shaderProgram, fragmentShader);
    glLinkProgram(shaderProgram);

    // check for linking errors
    glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);

    if (!success)
    {
        glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog);
        std::cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n"
                  << infoLog << std::endl;
    }

    glDeleteShader(vertexShader);
    glDeleteShader(fragmentShader);

    // set up vertex data (and buffer(s)) and configure vertex attributes
    // ------------------------------------------------------------------
    float vertices[] = { // 三角形的三个顶点
        -0.5f, -0.5f, +0.0f, // left
        +0.5f, -0.5f, +0.0f, // right
        +0.0f, +0.5f, +0.0f  // top
    };

    unsigned int VBO, VAO;
    glGenVertexArrays(1, &VAO);
    glGenBuffers(1, &VBO);
    
    // bind the Vertex Array Object first, then bind and set vertex buffer(s), and then configure vertex attributes(s).
    glBindVertexArray(VAO);

    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void *)0);
    glEnableVertexAttribArray(0);

    // note that this is allowed, the call to glVertexAttribPointer registered VBO as the vertex attribute's bound vertex buffer object so afterwards we can safely unbind
    glBindBuffer(GL_ARRAY_BUFFER, 0);

    // You can unbind the VAO afterwards so other VAO calls won't accidentally modify this VAO, but this rarely happens. Modifying other
    // VAOs requires a call to glBindVertexArray anyways so we generally don't unbind VAOs (nor VBOs) when it's not directly necessary.
    glBindVertexArray(0);

    // uncomment this call to draw in wireframe polygons.
    // glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);

    // render loop
    // 【这个有意思,web3d 走的是 requestAnimationFrame,也就是大概 1000ms / 60次 = 16ms/次, 间隔 16ms 调用一次】
    // 这里呢?无时无刻可调用,谁电脑性能强谁就跑的FPS就强,牛哈!!!webgl webgpu 敢不敢这么玩?
    while (!glfwWindowShouldClose(window))
    {
        // input
        // -----
        processInput(window);

        // render
        // ------
        glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);

        // draw our first triangle
        glUseProgram(shaderProgram);
        glBindVertexArray(VAO); // seeing as we only have a single VAO there's no need to bind it every time, but we'll do so to keep things a bit more organized
        glDrawArrays(GL_TRIANGLES, 0, 3);
        // glBindVertexArray(0); // no need to unbind it every time

        // glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.)
        // -------------------------------------------------------------------------------
        glfwSwapBuffers(window);
        glfwPollEvents();
    }

    // c / c++ 好习惯,用完要清理释放内存
    // optional: de-allocate all resources once they've outlived their purpose:
    // ------------------------------------------------------------------------
    glDeleteVertexArrays(1, &VAO);
    glDeleteBuffers(1, &VBO);
    glDeleteProgram(shaderProgram);

    // glfw: terminate, clearing all previously allocated GLFW resources.
    // ------------------------------------------------------------------
    glfwTerminate();
    return 0;
}

// process all input: query GLFW whether relevant keys are pressed/released this frame and react accordingly
// ---------------------------------------------------------------------------------------------------------
void processInput(GLFWwindow *window)
{
    if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
        glfwSetWindowShouldClose(window, true);
}

// glfw: whenever the window size changed (by OS or user resize) this callback function executes
// ---------------------------------------------------------------------------------------------
void framebuffer_size_callback(GLFWwindow *window, int width, int height)
{
    // make sure the viewport matches the new window dimensions; note that width and
    // height will be significantly larger than specified on retina displays.
    glViewport(0, 0, width, height);
}

项目跑不起来?项目配置可参考上篇:《Opengl – 图形学 – C/C++ 每天进步一点点【子鼠】》
点“调试”跑起来了

Hello Triangle
Hello Triangle
再画 Helloworld Shadertoy【非官方例子,自己原创实现】

只画了个三角形当然没意思,前阵我沉迷过 Shadertoy 直达我的 Shadertoy,话说回来,先把 shadertoy 的 helloworld 实现一下如何?之前我已经用 Webgl(Glsl) / Webgpu(Wgsl) 实现过一次,回顾一下关键代码
Webgl(Glsl) 版 Shadertoy

// webgl.glsl.js

/*
uniform vec3      iResolution;           // viewport resolution (in pixels)
uniform float     iTime;                 // shader playback time (in seconds)
*/

const shaderToy = /* glsl */`
void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
    // Normalized pixel coordinates (from 0 to 1)
    vec2 uv = fragCoord / iResolution.xy;

    // Time varying pixel color
    vec3 col = 0.5 + 0.5 * cos(iTime + uv.xyx + vec3(0, 2, 4));

    // Output to screen
    fragColor = vec4(col, 1.0);
}
`;

export const vs =  /* glsl */`
attribute vec2 a_position;

void main() {
  gl_Position = vec4(a_position, 0, 1);
}
`;

export const fs = /* glsl */ `
precision mediump float;
uniform vec2 iResolution;
uniform float iTime;

${shaderToy}

void main() {
  vec4 fragColor = vec4(0, 0, 0, 1.0);

  mainImage(fragColor, gl_FragCoord.xy);

  gl_FragColor = fragColor;
}
`;

Webgpu(Wgsl) 版 Shadertoy

// shadertoy.wgsl.js
/*
uniform vec3      iResolution;           // viewport resolution (in pixels)
uniform float     iTime;                 // shader playback time (in seconds)
*/

export const vs =  /* wgsl */`
@vertex
fn main(
  @builtin(vertex_index) vertexIndex : u32
) -> @builtin(position) vec4<f32> {
  var pos = array<vec2f, 6>(

    vec2f( -1.0,  -1.0),  // left, bottom
    vec2f( 1.0,  -1.0),  // right, bottom
    vec2f( -1.0,  1.0),  // left, top

    // 2st triangle
    vec2f( -1.0,  1.0),  // left, top
    vec2f( 1.0,  -1.0),  // right, bottom
    vec2f( 1.0,  1.0),  // right, top
  );

  return vec4<f32>(pos[vertexIndex], 0.0, 1.0);
}

`;

const shaderToy = /* wgsl */`
fn mainImage( fragCoord: vec2f, uni: Uniforms ) -> vec4f {
  let iResolution = uni.iResolution;
  let iTime = uni.iTime;

  // Normalized pixel coordinates (from 0 to 1)
  let uv = fragCoord / iResolution.xy;

  // Time varying pixel color
  let col = 0.5 + 0.5 * cos(iTime + vec3f(uv.xy, uv.x) + vec3f(0, 2, 4));

  return vec4f(col, 1.0);
}
`;

export const fs = /* wgsl */ `
struct Uniforms {
  iResolution: vec2f,
  iTime: f32,
};

@group(0) @binding(0) var<uniform> uni: Uniforms;

${shaderToy}

@fragment
fn main(
  @builtin(position) position: vec4f
) -> @location(0) vec4f {
  var fragColor = vec4f(0, 0, 0, 1.0);
  let fragCoord = position.xy;

  fragColor = mainImage(fragCoord, uni);

  return fragColor;
}

`;

思路都是差不多,就是把时间 iTime 与 屏幕分辨率(宽高)通过 uniform 传进 shader,然后所有的画图都在 frag shader 里面用 GPU 来画。
废话不多说,这就把 桌面客户端的 Helloworld Shadertoy 给办了。
C++ Opengl 版本的 Helloworld Shadertoy 的实现

1、引入 LearnOpengl.com 教程的 Shader 类

#include <learnopengl/shader_m.h>
这个类,只有一个 head,实现也全在 head 里面,注意它里面引入了 glm 图形计算的数学库类 #include <glm/glm.hpp>,这个类如果是 web 端就类似 gl-matrix.js 这个数学库。
shader_m.h 这个类主要实现了 Shader Program 的常用功能,如加载与编译 shader vs 与 shader fs 文件,创建 shader Program 并把 VERTEX FRAGMENT PROGRAM 一起链接起来。

2、进入主题,主要代码

先看 c++ 代码

// main.cpp
#include <glad/glad.h>
#include <GLFW/glfw3.h>

#include <learnopengl/shader_m.h>

#include <iostream>

void framebuffer_size_callback(GLFWwindow *window, int width, int height);
void processInput(GLFWwindow *window);

// settings
const unsigned int SCR_WIDTH = 1400;
const unsigned int SCR_HEIGHT = 1200;

// timing
float deltaTime = 0.0f;
float lastFrame = 0.0f;

int main()
{
    // glfw: initialize and configure
    // ------------------------------
    glfwInit();
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

#ifdef __APPLE__
    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
#endif

    // glfw window creation
    // --------------------
    GLFWwindow *window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "Hello Shadertoy", NULL, NULL);
    if (window == NULL)
    {
        std::cout << "Failed to create GLFW window" << std::endl;
        glfwTerminate();
        return -1;
    }
    glfwMakeContextCurrent(window);
    glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);

    // glad: load all OpenGL function pointers
    // ---------------------------------------
    if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
    {
        std::cout << "Failed to initialize GLAD" << std::endl;
        return -1;
    }

    // build and compile our shader program
    // ------------------------------------
    Shader ourShader("../src/shader.vs", "../src/shader.fs"); // you can name your shader files however you like

    // set up vertex data (and buffer(s)) and configure vertex attributes
    // ------------------------------------------------------------------
    // float vertices[] = {
    //     // positions         // colors
    //     0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f,  // bottom right
    //     -0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, // bottom left
    //     0.0f, 0.5f, 0.0f, 0.0f, 0.0f, 1.0f    // top
    // };

    // 这里我们把上面的例子的画三角形,改为用画两三角形变成全屏的长方形
    float vertices[] = {
        // positions         // colors
        // 1st triangle
        -1.0f, -1.0f, 0.0f, 1.0f, 0.0f, 0.0f, // left, bottom
        1.0f, -1.0f, 0.0f, 1.0f, 0.0f, 0.0f,  // right, bottom
        -1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f,  // left, top

        // 2ed triangle
        -1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, // left, top
        1.0f, -1.0f, 0.0f, 1.0f, 0.0f, 0.0f, // right, bottom
        1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f,  // right, top
    };

    unsigned int VBO, VAO;
    glGenVertexArrays(1, &VAO);
    glGenBuffers(1, &VBO);
    // bind the Vertex Array Object first, then bind and set vertex buffer(s), and then configure vertex attributes(s).
    glBindVertexArray(VAO);

    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

    // position attribute
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void *)0);
    glEnableVertexAttribArray(0);
    // color attribute
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void *)(3 * sizeof(float)));
    glEnableVertexAttribArray(1);

    // You can unbind the VAO afterwards so other VAO calls won't accidentally modify this VAO, but this rarely happens. Modifying other
    // VAOs requires a call to glBindVertexArray anyways so we generally don't unbind VAOs (nor VBOs) when it's not directly necessary.
    // glBindVertexArray(0);

    // shader configuration
    // --------------------
    // 这里把我们 shader 用到的 iTime, iResolution 作为 uniform 传入使用
    ourShader.use();
    ourShader.setFloat("iTime", 0.0f);
    ourShader.setVec2("iResolution", (float)SCR_WIDTH, (float)SCR_HEIGHT);

    // render loop
    // -----------
    while (!glfwWindowShouldClose(window))
    {
        // per-frame time logic
        // --------------------
        float currentFrame = static_cast<float>(glfwGetTime());
        deltaTime = currentFrame - lastFrame;
        lastFrame = currentFrame;

        // input
        // -----
        processInput(window);

        // render
        // ------
        glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);

        // render the shadertoy helloworld
        ourShader.use();
        ourShader.setFloat("iTime", currentFrame); // 这里把我们 shader 用到的实时变化的 iTime 作为 uniform 传入使用
        // ourShader.setVec2("iResolution", (float)SCR_WIDTH, (float)SCR_HEIGHT); // TODO: 监听窗口变化实时改变,这里先不实现,写死先
        glBindVertexArray(VAO);
        glDrawArrays(GL_TRIANGLES, 0, 6); // 注意这里要用 6, 我们要一次画 6 个点,两个三角形组成一个四边形嘛

        // glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.)
        // -------------------------------------------------------------------------------
        glfwSwapBuffers(window);
        glfwPollEvents();
    }

    // optional: de-allocate all resources once they've outlived their purpose:
    // ------------------------------------------------------------------------
    glDeleteVertexArrays(1, &VAO);
    glDeleteBuffers(1, &VBO);

    // glfw: terminate, clearing all previously allocated GLFW resources.
    // ------------------------------------------------------------------
    glfwTerminate();
    return 0;
}

// process all input: query GLFW whether relevant keys are pressed/released this frame and react accordingly
// ---------------------------------------------------------------------------------------------------------
void processInput(GLFWwindow *window)
{
    if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
        glfwSetWindowShouldClose(window, true);
}

// glfw: whenever the window size changed (by OS or user resize) this callback function executes
// ---------------------------------------------------------------------------------------------
void framebuffer_size_callback(GLFWwindow *window, int width, int height)
{
    // make sure the viewport matches the new window dimensions; note that width and
    // height will be significantly larger than specified on retina displays.
    glViewport(0, 0, width, height);
}

再看 shader.vs 与 shader.fs 代码

// shader.vs
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aColor;

out vec3 ourColor;

void main()
{
    gl_Position = vec4(aPos, 1.0);
    ourColor = aColor;
}
// shader.fs
#version 330 core
out vec4 FragColor;

in vec3 ourColor;

uniform float iTime;
uniform vec2 iResolution;

void main() {
    // Normalized pixel coordinates (from 0 to 1)
    vec2 uv = gl_FragCoord.xy / iResolution.xy;
    // Time varying pixel color
    vec3 col = 0.5 + 0.5 * cos(iTime + uv.xyx + vec3(0, 2, 4));

    FragColor = vec4(col, 1.0f);
}

项目目录结构如下:

Helloworld Shadertoy 目录结构
Helloworld Shadertoy 目录结构

用 cmake 调试,成了:

Helloworld Shadertoy 运行结果
Helloworld Shadertoy 运行结果

什么是 Shadertoy, 让 gpt 科普下:
Prompt: 介绍一下 Shadertoy
Answer:
Shadertoy是一个在线的交互式着色器编辑器和展示平台。它专注于图形编程和着色器开发领域,允许用户使用着色器语言编写和展示各种视觉效果。通过Shadertoy,用户可以在浏览器中创建复杂的图形效果和动画,并与全球的创作者社区分享和讨论他们的作品。

Shadertoy的核心是其着色器语言,它基于OpenGL Shading Language(GLSL),是一种用于编写图形着色器的高级语言。用户可以使用GLSL编写自定义的着色器代码,控制像素级别的图像处理和渲染。Shadertoy提供了一个用户友好的编辑器界面,使得编写和调试着色器代码变得更加容易。

在Shadertoy上,用户可以创建自己的着色器作品,调整各种参数和效果,实时预览结果,并与其他用户分享和讨论。平台上有一个活跃的创作者社区,可以浏览、搜索和评论其他人的作品。用户可以通过点赞和分享他们喜欢的作品,与其他创作者互动和学习。

除了在线编辑器和社区分享功能,Shadertoy还提供了一些额外的工具和特性,如音频分析和输入、相机和时间控制、材质库等,使得创作者能够更加丰富和个性化地表达他们的创意。

总的来说,Shadertoy是一个允许用户创建、分享和欣赏交互式着色器作品的平台,为图形编程和视觉效果的开发者提供了一个丰富的创作和学习空间。

【待续…】

作者: 博主

Talk is cheap, show me the code!

发表评论

邮箱地址不会被公开。

Captcha Code