Reputation: 1262
I want to render a terrain and apply colors depending on height.
I'm writing a Qt project, so use QGlShaderProgram.
My terrain grid is from (0,0,0) to (1000,0,1000) and vertices are placed every 100 length units. I wanted to transfer the data to the shader using an uniform array.
I still have problems sending data to the shader.
call from 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);
shader source:
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;
}
Where is my mistake?
How should I load the data to the shader and then access it there?
Upvotes: 5
Views: 4091
Reputation: 1262
(the code seems to work now)
I figured most of it out, with the help of izissise. I used GL_TEXTURE_RECTANGLE instead of GL_TEXTURE_2D.
Still it uses only the red channel (this might be optimized).
this is my Initialization:
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);
and sending data to shader (this may be repeated as often as wanted):
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();
as well as the shader source:
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;
}
Upvotes: 2
Reputation: 953
You want to pass it as a texture, you must first convert your array map (mHeightMap) in a opengl texture using glTexImage2D. look at this , it might be what your looking for: https://gamedev.stackexchange.com/questions/45188/how-can-i-pass-an-array-of-floats-to-the-fragment-shader-using-textures
Edit: You might want to tweak some of it, but it's the idea:
//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
Upvotes: 2