2

I've done Open GL ES in the past, but quite a while ago. From what I'm reading now, it seems there is a lot more emphasis on managing your own matrix stacks and pushing your matrices directly into a vertex shader to position your objects.

This is all well and good, but I'm just wondering how far you're meant to go with shaders. I don't know if they are only for simple things, or can I use them for a page turn effect. There will definitely be a lot of math that needs to be calculated. It feels like this is a good place to do it, but I don't have the experience to know if there are any gotchas with this.

I was going to input a point which maps to a finger position to the shader, then the shader would offset each vertex in a mesh which makes up the page, based on the finger position.

I'm going to attempt this in an Android App with OpenGl ES 2.0.

So,

The Question

Does this seem like a reasonable approach and if not, what would be the best way of getting the most out of the graphics chip given the effect I require and moden OpenGL APIs?

What I've researched so far

I've read through a number of blogs and tutorials, including Tutorials for Modern Open GL, but the shaders are always for lighting or just basic positioning. I'm thinking the calculations for a page turn effect might require more math than are in the shaders I've seen, but I don't know how much math in a shader would be too much!

I know I should just give this a go and see, but I've got a bit of time until this project starts and I was just really researching at the moment!

Update

Just as an update, I recently found out there is an instruction limit for shaders, so one requirement will be that I am able to get all my page turn math within the instruction limit.

Andy
  • 2,977
  • 2
  • 39
  • 71
  • I have implemented a page curl effect in an OpenGL ES 2.0 vertex shader and I will share this code with you. It uses the texture coordinates to control the rotation angle of the page. This way, the rotation is not affected by the position of the triangle mesh in the scene, only how far down the vertices are on the curled page. This works well, but I eventually abandoned this approach for a much simpler implementation where the curl was faked with just a few triangles rendered flat over the page and using textures to fake some shadow effects. – ClayMontgomery Feb 14 '14 at 17:44
  • @ClayMontgomery Feel free to post your answer! :) – Andy Feb 17 '14 at 08:31
  • To the down-voters, can you give me some constructive feedback and I'll try and improve the question? :) – Andy Feb 17 '14 at 08:32
  • Just noticed I got down-voted again without a comment. I've edited it again and tried to make it more narrow and less subjective! Please re-consider your down-vote. Down-votes do seem to breed more down-votes. – Andy Feb 17 '14 at 09:03

1 Answers1

6

I have implemented a page curl effect in an OpenGL ES 2.0 vertex shader and I will share this code with you. It uses the texture coordinates to control the rotation angle of the page. This way, the rotation is not affected by the position of the triangle mesh in the scene, only how far down the vertices are on the curled page. This works well, but I eventually abandoned this approach for a much simpler implementation where the curl was faked with just a few triangles rendered flat over the page and using textures to fake some shadow effects. Having done it both ways, I suspect that probably all of the page curl effects out there are done the fake way because it is just much simpler to implement and debug.

uniform mat4 transform_matrix;
uniform highp float radius;
uniform mediump vec2 axis;
uniform mediump vec2 axis_origin;
uniform highp float tex_size;

attribute vec2 texCoord;

varying vec2 texCoordVarying;
varying float shadeVarying;

mat4 rotationxy(float angle, vec2 axis);
mat4 translationThenRotationZ(float x, float y, float z, float angle);

void main()
{
    vec3 axisOrigin = vec3(axis_origin, radius);
    vec2 offsetTexCoord = texCoord - axis_origin;
    float angle = dot(offsetTexCoord, vec2(axis.y, -axis.x)) / radius;

    vec4 coord;
    vec4 myNormal = vec4(0.0, 0.0, 1.0, 1.0);

    if (angle <= 0.0) {                        // Flat section before curl
        coord = vec4(texCoord, 0.0, 1.0);
    } else {
        vec2 proj = dot(offsetTexCoord , axis) * (axis);
        const float PI = 3.14159265358979323846264;

        if (angle >= PI) {                    // Flat section after curl
            float axisAngle = asin(axis.x);
            proj += axis_origin;
            mat4 tr = translationThenRotationZ(proj.x, proj.y, 0.0, axisAngle);
            coord = vec4((PI - angle) * radius, 0.0, (radius + radius), 1.0);
            coord = tr * coord;

            myNormal.z = -1.0;
        } else {                            // Curl
            mat4 r = rotationxy(angle, axis);

            myNormal = r * myNormal;

            coord = vec4(proj, -axisOrigin.z, 1.0);
            r[3][0] = axisOrigin.x;
            r[3][1] = axisOrigin.y;
            r[3][2] = axisOrigin.z;
            coord = r * coord;
        }
    }
    gl_Position = transform_matrix * coord;

    shadeVarying = 0.25 + abs(myNormal.z) * 0.75;
    texCoordVarying = texCoord / tex_size;
}

mat4 rotationxy(float angle, vec2 axis)
{
    float x = axis.x;
    float y = axis.y;

    float xSqd = x * x;
    float ySqd = y * y;

    float xy = x * y;

    float cosAngle = cos(angle);
    float sinAngle = sin(angle);

    float xsinAngle = x * sinAngle;
    float ysinAngle = y * sinAngle;

    float oneMinusCos = 1.0 - cosAngle;

    float xyOneMinusCos = xy * oneMinusCos;

    mat4 target;
    target[0][0] = xSqd + (1.0 - xSqd)*cosAngle;
    target[0][1] = xyOneMinusCos;
    target[0][2] = ysinAngle;
    target[0][3] = 0.0;

    target[1][0] = xyOneMinusCos;
    target[1][1] = ySqd + (1.0 - ySqd)*cosAngle;
    target[1][2] = - xsinAngle;
    target[1][3] = 0.0;

    target[2][0] = - ysinAngle;
    target[2][1] = xsinAngle;
    target[2][2] = cosAngle;
    target[2][3] = 0.0;

    target[3][0] = 0.0;
    target[3][1] = 0.0;
    target[3][2] = 0.0;
    target[3][3] = 1.0;

    return target;
}

mat4 translationThenRotationZ(float x, float y, float z, float angle)
{
    float cosAngle = cos(angle);
    float sinAngle = sin(angle);

    mat4 target;
    target[0][0] = cosAngle;
    target[0][1] = -sinAngle;
    target[0][2] = 0.0;
    target[0][3] = 0.0;

    target[1][0] = sinAngle;
    target[1][1] = cosAngle;
    target[1][2] = 0.0;
    target[1][3] = 0.0;

    target[2][0] = 0.0;
    target[2][1] = 0.0;
    target[2][2] = 1.0;
    target[2][3] = 0.0;

    target[3][0] = x;
    target[3][1] = y;
    target[3][2] = z;
    target[3][3] = 1.0;

    return target;
}
ClayMontgomery
  • 2,786
  • 1
  • 15
  • 14