使用顶点着色器和QGLShaderProgram的OpenGL高度图

5

我希望能够呈现地形并根据高度应用颜色。
我正在编写一个Qt项目,使用QGlShaderProgram。

我的地形网格从(0,0,0)到(1000,0,1000),每隔100个长度单位放置一个顶点。我想使用统一数组将数据传输到着色器。

我仍然遇到了将数据发送到着色器的问题。

C++/Qt 中的调用:

QGLShaderProgram  mShader;
QVector< GLfloat> mHeightMap (10*10, some_data);
GLfloat           mXStepSize = 100;
GLfloat           mZStepSize = 100;
// ..
mShader.link();
mShader.bind();
mShader.setUniformValueArray( "heights",
                               &(mHeightMap[0]),       // one line after another
                               mHeightMap.size(), 1 );
mShader.setUniformValue( "x_res", (GLint) mXStepSize);
mShader.setUniformValue( "z_res", (GLint) mZStepSize);

着色器源代码:

uniform sampler2D heights;
uniform int       x_res;
uniform int       z_res;
void main(void)
{
       vec4 tmp         = gl_Vertex;
       vec4 h;
       float x_coord    = gl_Vertex[0] * 0.001;
       float z_coord    = gl_Vertex[2] * 0.001;

       // interprete as 2D:
       int element      = int( (x_coord + float(x_res)*z_coord) );

       h                = texture2D( heights, vec2(x_coord, z_coord));
       gl_FrontColor    = gl_Color;
       gl_FrontColor[1] = h[ element];     // set color by height
       tmp.y            = h[ element];     // write height to grid
       gl_Position      = gl_ModelViewProjectionMatrix * tmp;
}

我的错误在哪里?
我应该如何将数据加载到着色器中,然后在那里访问它?


可以使用mHeightMap.constData()代替&(mHeightMap[0])。 - Archie
使用mHeightMap.constData(),因为向量实际上更长,我传递了&(mHeigtMap[data_start])。 - hardmooth
你可以使用&(mHeightMap.constData()[data_start]),这样更安全。 - izissise
回答已编辑,应该更清晰明了。 - izissise
2个回答

2
如果您想将其作为纹理传递,必须先使用glTexImage2D将您的数组映射(mHeightMap)转换为OpenGL纹理。 看看这个链接,可能是您要找的:https://gamedev.stackexchange.com/questions/45188/how-can-i-pass-an-array-of-floats-to-the-fragment-shader-using-textures 编辑:您可能需要调整其中的一些内容,但这就是基本思路。
//Create texture: 
glint texture;
glActiveTexture(GL_TEXTURE0);
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);  
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);  
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);  
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);  
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);  
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, Width, Height, 0, GL_RGBA,
GL_UNSIGNED_BYTE, &(mHeightMap.constData()[data_start]));

//pass it to shader
glint uniformId = glGetUniformid(shader, "height");
glActiveTexture(GL_TEXTURE0);   
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, texture);  
glUniform1i(uniformId, 0); // 0 is the texture number

感谢您详细的回答,但还存在一些问题: 1)我只有OpenGL 3(所以没有glUniform1i) 2)我插入了一个浮点数数组,但这样在着色器中却得到了vec4的数组(如果我理解正确的话) - hardmooth
可以使用QGlShaderProgram来实现这个吗? - hardmooth
这段代码是使用OpenGL 3.1完成的,因此glUniform1i应该在GL3中。我不了解Qt,所以对QGlShaderProgram也不清楚。glTexImage2D用于纹理加载,我不认为它会出现在vec4中。 - izissise
我想通了 - 请看(我的)答案 - hardmooth

2

代码似乎现在可以工作了。

在 izissise 的帮助下,我解决了大部分问题。我使用 GL_TEXTURE_RECTANGLE 替代了 GL_TEXTURE_2D。
然而,它仅使用红色通道(这可能会进行优化)。

这是我的初始化

QGLShaderProgram    mShader;
QVector< GLfloat> mHeightMap (width * height * state_count, 
                              some_data);
mShader.link();

// init texture
glActiveTexture(GL_TEXTURE0);
glGenTextures(1, &mShaderTexture);
glBindTexture(GL_TEXTURE_RECTANGLE, mShaderTexture);


并且将数据发送给着色器(可以随意重复):

mShader.bind();
// ..
glTexImage2D(GL_TEXTURE_RECTANGLE, 0, GL_RED,
             width, depth, 0,
             GL_RED, GL_FLOAT,
             &(mHeightMap.constData()[mHeightMapPos])); // set portion of vector as array to texture / sampler
glActiveTexture(GL_TEXTURE0);
glEnable(GL_TEXTURE_RECTANGLE);
glBindTexture(GL_TEXTURE_RECTANGLE, mShaderTexture);
mShader.setUniformValue( "max_height",  (GLfloat) (250.0) );
mShader.setUniformValue( "x_steps",     (GLint) width);
mShader.setUniformValue( "z_steps",     (GLint) height);

// ..
mShader.release();


以及着色器源代码:

uniform int     x_steps;
uniform int     z_steps;
uniform sampler2DRect heights;
uniform float   max_height;

void main(void)
{
    vec4 tmp         = gl_Vertex;
    vec4 h;

    float x_coord    = gl_Vertex[0] * 0.001 * float(x_steps-1);
    float z_coord    = gl_Vertex[2] * 0.001 * float(z_steps-1);
    h                = texture2DRect( heights, ivec2(int(x_coord), int(z_coord)) );
    tmp.y            = max_height * (h.r);

    gl_FrontColor    = gl_Color;
    gl_FrontColor[1] = h.r;

    gl_Position      = gl_ModelViewProjectionMatrix * tmp;
}

太好了!顺便说一下,这些行: glTexParameteri(GL_TEXTURE_RECTANGLE, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_RECTANGLE, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_RECTANGLE, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_RECTANGLE, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 不是必要的,更多是为了获得更好的纹理渲染效果,但由于你所拥有的不是真正的纹理,我认为你可以将它们删除。 - izissise
另外,在您的代码示例中,我不明白 uniform sampler2DRect heights; 如何绑定到正确的纹理。有什么线索吗? - izissise
@izissise:我删除了那4行;对于绑定:我不确定,但我认为只有1个纹理,所以这不是问题。然而,高度没有正确传递到着色器中,或者我在读取它们时错过了什么 - 一旦我弄清楚了,我会更新代码。 - hardmooth
我做了一些其他的测试,然后我忘了纹理的插值 - 它应该按照上述方式工作。 - hardmooth

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接