Search code examples
objective-copengl-estexturesvertex-shader

How do I pass texture data to this openGL vertex shader from an Objective C client?


I'm trying to follow an example from "3D Engine Design for Virtual Globes" by Cozzi and Ring.

I'm trying to use their vertex shader (11.2.2, p. 319), as it seems to provide exactly the starting point for what I need to accomplish (rendering terrain from dense, array-based terrain data):

in vec2 position;
uniform mat4 og_modelViewPerspectiveMatrix;
uniform sampler2DRect u_heightMap;

void main()
{
    gl_Position = og_modelViewPerspectiveMatrix * vec4(position, texture(u_heightMap,    position).r, 1.0);
}

The problem is that I'm not clear how to set up the necessary data in the Objective C client code. Build output shows

TerrainShaderTest[10429:607] Shader compile log:
ERROR: 0:31: Invalid qualifiers 'in' in global variable context
ERROR: 0:33: 'sampler2DRect' : declaration must include a precision qualifier for type
ERROR: 0:37: Use of undeclared identifier 'position'
ERROR: 0:37: Use of undeclared identifier 'u_heightMap'
ERROR: 0:37: Use of undeclared identifier 'position'
2015-01-08 10:33:30.532 TerrainShaderTest[10429:607] Failed to compile vertex shader
2015-01-08 10:33:30.545 TerrainShaderTest[10429:607] GL ERROR: 0x0500

If I use a different vertex shader instead (below), I get a basic but working result, using the same set up for position on the client side (but obviously not heightmap/texture.)

// WORKS - very simple case
attribute vec4 position;
varying lowp vec4 colorVarying;
uniform mat4 modelViewProjectionMatrix;

void main()
{
    colorVarying = (position + vec4(0.5,0.5,0.0,0));
    gl_Position = modelViewProjectionMatrix * position;
}

Code snippets from client (Objective C):

...
glUniformMatrix4fv(uniforms[UNIFORM_MODELVIEWPROJECTION_MATRIX], 1, 0, _modelViewProjectionMatrix.m);
...

#pragma mark -  OpenGL ES 2 shader compilation

- (BOOL)loadShaders
{
    GLuint vertShader, fragShader;
    NSString *vertShaderPathname, *fragShaderPathname;

    // Create shader program.
    _program = glCreateProgram();

    // Create and compile vertex shader.
    vertShaderPathname = [[NSBundle mainBundle] pathForResource:@"Shader" ofType:@"vsh"];
    if (![self compileShader:&vertShader type:GL_VERTEX_SHADER file:vertShaderPathname]) {
        NSLog(@"Failed to compile vertex shader");
        return NO;
    }

    // Create and compile fragment shader.
    fragShaderPathname = [[NSBundle mainBundle] pathForResource:@"Shader" ofType:@"fsh"];
    if (![self compileShader:&fragShader type:GL_FRAGMENT_SHADER file:fragShaderPathname]) {
        NSLog(@"Failed to compile fragment shader");
        return NO;
    }

    // Attach vertex shader to program.
    glAttachShader(_program, vertShader);

    // Attach fragment shader to program.
    glAttachShader(_program, fragShader);

    // Bind attribute locations.
    // This needs to be done prior to linking.
    glBindAttribLocation(_program, GLKVertexAttribPosition, "position");

    // Link program.
    if (![self linkProgram:_program]) {
        NSLog(@"Failed to link program: %d", _program);

        if (vertShader) {
            glDeleteShader(vertShader);
            vertShader = 0;
        }
        if (fragShader) {
            glDeleteShader(fragShader);
            fragShader = 0;
        }
        if (_program) {
            glDeleteProgram(_program);
            _program = 0;
        }

        return NO;
    }

    // Get uniform locations.
    uniforms[UNIFORM_MODELVIEWPROJECTION_MATRIX] = glGetUniformLocation(_program, "modelViewProjectionMatrix");
    uniforms[UNIFORM_TEXTURE] = glGetUniformLocation(_program, "u_heightMap");
    glProgramUniform1fvEXT (_program, uniforms[UNIFORM_TEXTURE], nTerrainElements, _pTerrainScaled);

    // Release vertex and fragment shaders.
    if (vertShader) {
        glDetachShader(_program, vertShader);
        glDeleteShader(vertShader);
    }
    if (fragShader) {
        glDetachShader(_program, fragShader);
        glDeleteShader(fragShader);
    }

    return YES;
}

Any help on setting up the client side data?


Solution

  • On iOS, you will be using OpenGL ES. The most commonly used version is ES 2.0. Devices released starting in 2013 also support ES 3.0.

    Your shader code is not compatible with ES 2.0:

    in vec2 position;
    uniform mat4 og_modelViewPerspectiveMatrix;
    uniform sampler2DRect u_heightMap;
    
    void main()
    {
        gl_Position = og_modelViewPerspectiveMatrix *
                      vec4(position, texture(u_heightMap, position).r, 1.0);
    }
    

    To make this work with ES 2.0:

    • ES 2.0 still uses attribute for vertex attributes, instead of in like current versions of full OpenGL, and ES 3.0+.
    • No version of ES supports RECT textures, so the type sampler2DRect is invalid. Use regular 2D textures, with the corresponding sampler2D in the shader code, instead.
    • ES 2.0 uses texture2D() as the name of the texture sampling function, instead of texture() in newer versions.

    The shader should then look like this:

    attribute vec2 position;
    uniform mat4 og_modelViewPerspectiveMatrix;
    uniform sampler2D u_heightMap;
    
    void main()
    {
        gl_Position = og_modelViewPerspectiveMatrix *
                      vec4(position, texture2D(u_heightMap, position).r, 1.0);
    }