让老式的 OpenGL 代码在 GLSL 中工作

Get old style OpenGL code work in GLSL

我正在尝试在 OpenGL 中绘制此图案:

为了得到这个,我创建了这样的模式:

vector< vector<DataPoint> > datas;
float Intensitytemp=0;
float xPos=0, yPos=0, angleInRadians=0;
for (float theta = 0.0f; theta < 4096; theta += 1.f)
{
    vector<DataPoint> temp;
    angleInRadians = 2 * M_PI*theta / 4096;
    for (float r = 0; r < 4096; r += 1.f)
    {
        xPos = cos(angleInRadians)*r / 4096;
        yPos = sin(angleInRadians)*r / 4096;
        Intensitytemp = ((float)((int)r % 256)) / 255;
        DataPoint dt;
        dt.x = xPos;
        dt.y = yPos;
        dt.Int = Intensitytemp;
        temp.push_back(dt);
    }
    datas.push_back(temp);
}

并且我将图案绘制为:

glBegin(GL_POINTS);
    for (int x = 0; x < 4096; x++)
        for (int y = 0; y < 4096; y++)
        {
            xPos = datas[x][y].x;
            yPos = datas[x][y].y;
            Intensitytemp = datas[x][y].Int;
            glColor4f(0.0f, Intensitytemp, 0.0f, 1.0f);
            glVertex3f(xPos, yPos, 0.0f);
        }
glEnd();

如果我在 glBegin()-glEnd() 块中创建数据,它会工作得更快。但在这两种情况下,我认为更好的方法是在 GLSL.I 中完成所有操作,我没有很好地理解现代 OpenGL 背后的逻辑。

我尝试创建顶点缓冲区数组和颜色数组,但无法正常工作。问题不在于将数组传输到图形 card.I 我在数组中出现计算器溢出。 这是另一个主题的问题,但我想知道是否可以用完全 GLSL 代码(那些在 .vert 文件中的代码) 来完成这个任务而不将这些巨大的数组转移到显卡上。

  1. 渲染覆盖屏幕的四边形

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    
    GLint id;
    glUseProgram(prog_id);
    
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glMatrixMode(GL_TEXTURE);
    glLoadIdentity();
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    
    glDisable(GL_DEPTH_TEST);
    glDisable(GL_TEXTURE_2D);
    
    glBegin(GL_QUADS);
    glColor3f(1,1,1);
    glVertex2f(-1.0,-1.0);
    glVertex2f(-1.0,+1.0);
    glVertex2f(+1.0,+1.0);
    glVertex2f(+1.0,-1.0);
    glEnd();
    
    glUseProgram(0);
    glFlush();
    SwapBuffers(hdc);
    

    另见 如何让 GLSL 工作(甚至是新东西)

    不要忘记将 GL 视图设置为方形区域!

  2. 在顶点着色器中将顶点坐标传递给片段[=​​94=]

    不需要矩阵...pos<-1.0,1.0> 范围内,这对片段来说很好。

    // Vertex
    varying vec2 pos;
    void main()
        {
        pos=gl_Vertex.xy;
        gl_Position=gl_Vertex;
        }
    
  3. 在片段中计算距中间的距离(0,0)并从中计算最终颜色

    // Fragment
    varying vec2 pos;
    void main()
        {
        vec4 c=vec4(0.0,0.0,0.0,1.0);
        float r=length(pos);    // radius = distance to (0,0)
        if (r<=1.0)             // inside disc?
            {
            r=16.0*r;           // your range 16=4096/256
            c.g=r-floor(r);     // use only the fractional part ... %256
            }
        gl_FragColor=c;
        }
    

    这里是结果:

  4. GLSL 的工作原理

    您可以将 片段着色器 作为多边形填充的颜色计算引擎来处理。它是这样工作的:

    GL原语由GL调用传递给顶点着色器,它负责常量的转换和预计算。 Vertex shader 为来自旧式 GL 的每个 glVertex 调用调用。

    如果支持的基元(由 glBegin 在旧式 GL 中设置)完全通过(如 TRIANGLE、QUAD、...),gfx 卡开始光栅化。这是通过 HW 插值器为每个 "pixel" 调用 片段着色器 来完成的。由于 "pixel" 包含的数据比颜色多得多,而且也可以丢弃……它被称为 fragment。它的唯一目的是计算它所代表的屏幕上像素的目标颜色。你不能只改变它的位置颜色。这是旧 GLGLSL 方法之间最大的区别。您无法更改对象的形状或位置,只能更改它们的状态 colored/shaded 因此得名 shaders。因此,如果您需要生成特定的图案或效果,您通常会渲染一些覆盖 GL 涉及的区域的图元,并通过主要在 片段着色器 中的计算对其重新着色.

    显然,在大多数情况下,顶点着色器的调用频率不如片段着色器,因此请尽可能多地移动计算顶点着色器以提高性能。

    较新的 GLSL 版本还支持几何和 曲面细分着色器 但这本身就是一个章节,现在对您来说并不重要。 (你需要先习惯Vertex/Fragment)。

[注释]

单个if在如此简单的着色器中问题不大。主要的速度提升在于您通过了单个四边形而不是 4096x4096 点。 Shader代码直接由gfxHW完全并行化。这就是为什么架构是这样的......与标准 CPU/MEM 架构相比,限制了着色器内部可以有效完成的某些功能。

[编辑 1]

您通常可以通过以下巧妙的数学技巧来避免 if

// Fragment
varying vec2 pos;
void main()
    {
    vec4 c=vec4(0.0,0.0,0.0,1.0);
    float r=length(pos);            // radius = distance to (0,0)
    r*=max(1.0+floor(1.0-r),0.0);   // if (r>1.0) r=0.0;
    r*=16.0;                        // your range 16=4096/256
    c.g=r-floor(r);                 // use only the fractional part ... %256
    gl_FragColor=c;
    }

直接回答你的问题

不,现在着色器就是这样工作的。着色器重新定义了渲染管线的一部分。在古老的 OpenGL 中,管道是固定的,GPU 使用内置着色器例程来渲染您通过 glBegin/glEnd 相关调用上传到它的图元。但是,对于更高版本的 OpenGL,您可以编写供 GPU 使用的自定义例程。 在这两种情况下您需要为着色器发送数据。

让您更好地了解如何去做

首先,顶点着色器以顶点数据为基础。它获取顶点并一次对它们进行操作,应用各种变换(通过将顶点乘以模型-视图-投影矩阵)。一旦它为每个顶点完成此操作,通过连接顶点形成的区域将在称为 rasterization 的过程中分解为坐标值(除其他外,您可以从顶点着色器传递到片段着色器)。这些坐标,每个代表屏幕上一个像素的位置,然后被发送到片段着色器,片段着色器对其进行操作以设置颜色并应用任何光照计算。

现在,由于您要绘制的是一个背后有公式的图案,您可以通过只发送 4 个顶点来为 4096x4096 正方形着色,因为您希望产生相同的结果。

顶点着色器:

#version 150

in vec2 vertexPos;
out vec2 interpolatedVertexPos;

void main()
{
  interpolatedVertexPos = vertexPos;
}

片段着色器:

#version 1.5

in vec2 interpolatedVertexPos;
out vec4 glFragColor;

void main()
{
  const vec2 center = vec2(2048, 2048);
  float distanceFromCenter = sqrt(pow((interpolatedVertexPos.x-center.x), 2) + pow((interpolatedVertexPos.y-center.y), 2));
  if (distanceFromCenter > 2048){
    discard;
  }
  float Intensitytemp = ((float)((int)distanceFromCenter % 256)) / 255;
  glFragColor = vec4(0, Intensitytemp , 0, 1);
}

编辑:我想您可能会发现这个答案很有帮助: