目前我有一个小型 C++ 应用程序,它通过这两个着色器显示三角形:
顶点着色器:
static const char *vertex_shader_glsl = \
"#version 430\n"
"layout (location=0)in vec2 inVer;"
"out vec2 p;"
"out gl_PerVertex"
"{"
"vec4 gl_Position;"
"};"
"void main()"
"{"
"gl_Position=vec4(inVer,0.0,1.0);"
"p=inVer;"
"}";
片段着色器:
#version 430
out vec4 fragColor;
in vec2 fragCoord;
vec2 iResolution;
//uniform float iGlobalTime;
float a = -2;
void main()
{
a = sin(-2);
//Colors
vec3 fgColor = vec3(0.741, 0.635, 0.471);
vec3 bgColor = vec3(0.192, 0.329, 0.439);
//Triangle barycentric coordinates defined on screen space
vec2 t0 = vec2(0.25, 0);
vec2 t1 = vec2(0.75, 0.25);
vec2 t2 = vec2(0.50, 0.85);
vec2 tCentroid = (t0 + t1 + t2)/3.0;
//Compute UV coordinates
vec2 uv = fragCoord.xy;
vec2 v0 = t2 - t0;
vec2 v1 = t1 - t0;
vec2 v2 = uv - t0;
//Compute barycentric coordinates
float dot00 = dot(v0, v0);
float dot01 = dot(v0, v1);
float dot02 = dot(v0, v2);
float dot11 = dot(v1, v1);
float dot12 = dot(v1, v2);
float invDenom = 1.0/(dot00 * dot11 - dot01 * dot01);
float baryX = (dot11 * dot02 - dot01 * dot12) * invDenom;
float baryY = (dot00 * dot12 - dot01 * dot02) * invDenom;
if((baryX >= 0.0) && (baryY >= 0.0) && (baryX + baryY <= 1.0)) {
fragColor = vec4(fgColor, 1.0);
} else {
fragColor = vec4(bgColor, 1.0);
}
}
这两个着色器显示一个简单的三角形:
但是我想旋转这个三角形,所以我需要一些计时器实现(iGlobalTime)。
我的意思是,我知道我需要这样的东西: https://www.shadertoy.com/view/Xtt3WX
但是,我无法从上面的链接复制粘贴代码,因为我无法在我的 C++ 项目中使用 iGlobalTime。如何更新上面的着色器以便能够根据系统时间旋转三角形?如何在非常基本的 C++ 应用程序中实现 iGlobalTime?
============================
已编辑
我已更新:
扩展名:
...
#ifdef DEBUG
#define NUMFUNCTIONS 14 // <<== HERE
#else
#define NUMFUNCTIONS 12 // <<== HERE
#endif
extern void *myglfunc[NUMFUNCTIONS];
#define glCreateShaderProgramv ((PFNGLCREATESHADERPROGRAMVPROC)myglfunc[0])
#define glGenProgramPipelines ((PFNGLGENPROGRAMPIPELINESPROC)myglfunc[1])
#define glBindProgramPipeline ((PFNGLBINDPROGRAMPIPELINEPROC)myglfunc[2])
#define glUseProgramStages ((PFNGLUSEPROGRAMSTAGESPROC)myglfunc[3])
#define glProgramUniform4fv ((PFNGLPROGRAMUNIFORM4FVPROC)myglfunc[4])
#define glGenFramebuffers ((PFNGLGENFRAMEBUFFERSPROC)myglfunc[5])
#define glBindFramebuffer ((PFNGLBINDFRAMEBUFFERPROC)myglfunc[6])
#define glTexStorage2D ((PFNGLTEXSTORAGE2DPROC)myglfunc[7])
#define glDrawBuffers ((PFNGLDRAWBUFFERSPROC)myglfunc[8])
#define glFramebufferTexture ((PFNGLFRAMEBUFFERTEXTUREPROC)myglfunc[9])
#define glProgramUniform1f ((PFNGLPROGRAMUNIFORM1FPROC)myglfunc[10]) // <<== ADDED NEW LINE HERE
#define glGetUniformLocation ((PFNGLGETUNIFORMLOCATIONPROC)myglfunc[11]) // <<== ADDED NEW LINE HERE
#ifdef DEBUG
#define glGetProgramiv ((PFNGLGETPROGRAMIVPROC)myglfunc[12])
#define glGetProgramInfoLog ((PFNGLGETPROGRAMINFOLOGPROC)myglfunc[13])
#endif
...
更新了 ext.cpp:
...
static char *strs[] = {
"glCreateShaderProgramv",
"glGenProgramPipelines",
"glBindProgramPipeline",
"glUseProgramStages",
"glProgramUniform4fv",
"glProgramUniform1fv",
"glGenFramebuffers",
"glBindFramebuffer",
"glTexStorage2D",
"glDrawBuffers",
"glFramebufferTexture",
"glProgramUniform1f", // <<== ADDED NEW LINE HERE
"glGetUniformLocation", // <<== ADDED NEW LINE HERE
#ifdef DEBUG
"glGetProgramiv",
"glGetProgramInfoLog",
#endif
};
...
更新了 intro.cpp:
...
void intro_do(long time)
{
...
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
glBindProgramPipeline(renderingPipeline);
// Set fparams to give input to shaders
// Render
glProgramUniform4fv(fragmentShader, 0, 4, fparams);
glProgramUniform1f(fragmentShader, glGetUniformLocation(fragmentShader, "iGlobalTime"), time*1000); // <<== ADDED NEW LINE HERE
glRects(-1, -1, 1, 1); // Deprecated. Still seems to work though.
...
}
应用程序现在可以运行,但立即崩溃,并显示:“4kIntro.exe 中 0x7542CB49 处出现未处理的异常:0xC0000005:读取位置 0x00000000 时发生访问冲突。”
最佳答案
你可以这样做(你需要一个符合 c++11 的编译器)
#include <chrono>
int main()
{
// init stuff
// game loop
using Clock = std::chronohigh_resolution_clock;
using TimePoint = std::chrono::time_point<Clock>;
TimePoint t0, t1;
float global_time = 0;
t0 = Clock::now();
while (run_game)
{
// dt is the time since last frame
// global_time is the total time since the start of the game loop
t1 = Clock::now();
float dt = std::chrono::duration_cast<std::chrono::duration<float, std::chrono::seconds::period>>(t1 - t0);
global_time += dt;
// Send your uniform and kickoff rendering
}
return 0;
}
希望这会有所帮助(代码中可能有一些拼写错误,我没有检查)
编辑: 看起来你误解了一些东西,所以我会尝试更多地解释事情是如何发生的。
着色器
根据the OpenGL wiki着色器是“一种用户定义的程序,设计用于在图形处理器的某个阶段上运行,其目的是执行渲染管道的可编程阶段之一。”
这意味着,当您要求 GPU 绘制内容时(通过绘制调用,即 glDrawArrays
或 glDrawElements
)它将为每个顶点调用顶点着色器,然后为每个片段调用片段着色器。 (这是非常简单的,有关整个管道的更完整的解释可以引用 this OpenGL wiki )。
CPU和GPU之间的通信
在调用 glDrawArrays
或 glDrawElements
之前,您必须首先向 GPU 发送您希望其绘制的顶点。
您在 C++ 代码中做了类似的事情(这可能不是完全相同的代码,不用担心)
GLfloat data[] = { ... } // The data you want to send
GLuint vao, vbo;
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof data, data, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, (GLvoid*)0);
在这里,您将一些顶点数组发送到 GPU,并且每次发出绘制调用时,都会在其中的每个元素上执行您的顶点着色器。
在其中输出一个 gl_Position
,然后 GPU 将对其进行光栅化并输入到片段(或像素)着色器。在这里,对于每个片段/像素,您的着色器将被执行。
“这很好,但是我如何将全局数据发送到 GPU ?” 这就是制服发挥作用的地方。您可以使用它们将每个着色器的数据发送到 GPU。这是在您的 C++ 代码中完成的,类似于
GLuint location = glGetUniformLocation(program_id, "uniform_name");
glUniform_(location, uniform_data);
由于 OpenGL API 是用 C 编写的,因此所有 glUniform
调用都以您要发送的数据类型为后缀(有关所有调用,请参阅 the docs)。然后,发送数据后,您就可以在着色器中使用它。
请注意,在着色器中声明统一是不够的,您还必须将数据发送到 GPU。这与 C++ 中的任何变量相同,只要你不对其进行任何影响,它就不会具有可靠的值。
在您的情况下,由于您想发送一个名为 iGlobalTime
的 float 制服,您将得到类似的内容
GLuint time_loc = glUniformLocation(program_id, "iGlobalTime");
glUniform1f(time_loc, global_time)
更新后的 C++ 代码考虑了大部分内容
#include <chrono>
int main()
{
// init stuff
// Compile your shader_program and store its id in program_id
// Set the vertex data on the GPU
// game loop
using Clock = std::chronohigh_resolution_clock;
using TimePoint = std::chrono::time_point<Clock>;
TimePoint t0, t1;
float global_time = 0;
t0 = Clock::now();
while (!quit)
{
// dt is the time since last frame
// global_time is the total time since the start of the game loop
t1 = Clock::now();
float dt = std::chrono::duration_cast<std::chrono::duration<float, std::chrono::seconds::period>>(t1 - t0);
global_time += dt;
// Activate program
glUseProgram(program_id);
// Send global_time uniform
glUniform1f(glGetUniformLocation(program_id, "iGlobalTime"), global_time);
// Issue draw call
glBindVertexArray(vao);
glDrawArrays(GL_TRIANGLES, 0, 4);
glBindVertexArray(0);
}
return 0;
}
关于c++ - 如何在 C++ 中为 GLSL 片段着色器实现 iGlobalTime?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39759035/