如何一次性渲染大量彩色、旋转、缩放和平移的四边形?

发布于 2024-12-23 18:20:07 字数 1344 浏览 0 评论 0原文

我们这里讨论的是 2D 精灵,因此每个精灵有 4 个顶点(8 个 GLint)和 4 个纹理坐标(另外 8 个 GLint)。我有一个排序例程,它会输出可以一次渲染的精灵列表(它们具有相同的混合和相同的纹理,blablabla)。然而,每个精灵也有平移、旋转、缩放等。

我目前这样做(伪代码):

cdef int *vertices
cdef int *texcoords

bind_texture(texture)
set_blending_mode_and_other_blablabla(spritelist)

vertices = alloc_mem(len(sprites) * 8 * sizeof(GLint))
texcoords = alloc_mem(len(sprites) * 8 * sizeof(GLint))

glEnableClientState(GL_VERTEX_ARRAY)
glEnableClientState(GL_TEXTURE_COORD_ARRAY)

glTexCoordPointer(2, GL_INT, 0, texcoords)
glVertexPointer(2, GL_INT, 0, vertices)

load_vertices(sprites, vertices)
load_texcoords(sprites, texcoords)

for index, sprite in spritelist:
    glPushMatrix()

    glColor4f(sprite.red, sprite.green, sprite.blue, sprite.alpha)
    glTranslatef(int(sprite.x), int(sprite.y), 0.0)
    glRotatef(sprite.rotation, 0.0, 0.0, 1.0)
    glScalef(sprite.scale_x, sprite.scale_y, 1.0)
    glTranslatef(-int(sprite.anchor_x), -int(sprite.anchor_y), 0.0)

    glDrawArrays(GL_QUADS, 4 * i, 4)

    glPopMatrix()

现在,正如您可以猜到的,这并不是很快。我想一次性把所有东西都画出来。我真的不知道如何将平移、旋转等数据传递到 OpenGL 中。如果有人能指出一两个有效的渲染路径,那就太好了。

每个精灵的确切唯一数据为:

  • 颜色(红、绿、蓝、alpha)
  • 比例(x 和 y 因子)
  • 平移(x 和 y)
  • 旋转
  • Texcoords(8 个 GLint)
  • 顶点(8 GLint's)

请温柔一点,我是 OpenGL 新手。

We are talking 2D sprites here, so I have 4 vertices for each sprite (8 GLint's) and 4 texcoords (another 8 GLint's). I have a sorting routine which spits out lists of sprites that can be rendered in one pass (they have the same blending and same texture, blablabla). However, each sprite also has a translation, rotation, scale, etc.

I currently do this (pseudocode):

cdef int *vertices
cdef int *texcoords

bind_texture(texture)
set_blending_mode_and_other_blablabla(spritelist)

vertices = alloc_mem(len(sprites) * 8 * sizeof(GLint))
texcoords = alloc_mem(len(sprites) * 8 * sizeof(GLint))

glEnableClientState(GL_VERTEX_ARRAY)
glEnableClientState(GL_TEXTURE_COORD_ARRAY)

glTexCoordPointer(2, GL_INT, 0, texcoords)
glVertexPointer(2, GL_INT, 0, vertices)

load_vertices(sprites, vertices)
load_texcoords(sprites, texcoords)

for index, sprite in spritelist:
    glPushMatrix()

    glColor4f(sprite.red, sprite.green, sprite.blue, sprite.alpha)
    glTranslatef(int(sprite.x), int(sprite.y), 0.0)
    glRotatef(sprite.rotation, 0.0, 0.0, 1.0)
    glScalef(sprite.scale_x, sprite.scale_y, 1.0)
    glTranslatef(-int(sprite.anchor_x), -int(sprite.anchor_y), 0.0)

    glDrawArrays(GL_QUADS, 4 * i, 4)

    glPopMatrix()

Now, as you can guess, this isn't terribly fast. I'd like to draw everything in one pass. I really have no idea how to pass the translation, rotation, etc data into OpenGL. If someone could point out an efficient render path or two that would be very nice.

The exact per-sprite unique data is:

  • Color (red, green, blue, alpha)
  • Scale (x and y factor)
  • Translation (x and y)
  • Rotation
  • Texcoords (8 GLint's)
  • Vertices (8 GLint's)

Please be gentle, I'm new to OpenGL.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

故人的歌 2024-12-30 18:20:07

将精灵信息粘贴到顶点属性中,并将它们应用到顶点着色器中。

或者,您可以将缩放、平移和旋转计算移至 CPU 上,并将每帧预变换的顶点缓冲区传递给 GPU。这将减少 GL API 调用,但代价是增加 CPU 使用率。我推荐 Eigenglm 用于重型矩阵提升。

编辑:着色器解决方案:

// g++ main.cpp -lGLEW -lglut -lGL
#include <GL/glew.h>
#include <GL/glut.h>
#include <iostream>
#include <vector>
#include <cmath>
using namespace std;

// OpenGL Mathematics (GLM): http://glm.g-truc.net/
#include <glm/glm.hpp>
#include <glm/gtc/random.hpp>
using namespace glm;

// stores/manipulates a list of rectangular sprites and their vertexes
struct SpriteWrangler
{
    SpriteWrangler( unsigned int aSpriteCount )
    {
        verts.resize( aSpriteCount * 4 );
        states.resize( aSpriteCount );

        for( size_t i = 0; i < states.size(); ++i )
        {
            states[i].vel = linearRand( vec2( -30, -30 ), vec2( 30, 30 ) );
            states[i].rotvel = linearRand( -1.0f, 1.0f );

            Vertex vert;
            vert.pos = linearRand( vec2( -400, -400 ), vec2( 400, 400 ) );
            vert.dim = linearRand( vec2( 20, 20 ), vec2( 60, 60 ) );
            vert.rotation = linearRand( 0.0f, 2 * 3.14159f );
            vert.r = (unsigned char)linearRand( 64.0f, 255.0f );
            vert.g = (unsigned char)linearRand( 64.0f, 255.0f );
            vert.b = (unsigned char)linearRand( 64.0f, 255.0f );
            vert.a = 255;

            vert.meta = vec2( 5, 0 );
            verts[i*4 + 0] = vert;
            vert.meta = vec2( 15, 0 );
            verts[i*4 + 1] = vert;
            vert.meta = vec2( 25, 0 );
            verts[i*4 + 2] = vert;
            vert.meta = vec2( 35, 0 );
            verts[i*4 + 3] = vert;
        }
    }

    void wrap( const float minVal, float& val, const float maxVal )
    {
        if( val < minVal )
            val = maxVal - fmod( maxVal - val, maxVal - minVal );
        else
            val = minVal + fmod( val - minVal, maxVal - minVal );
    }

    void Update( float dt )
    {
        for( size_t i = 0; i < states.size(); ++i )
        {
            Vertex& vert = verts[i*4 + 0];
            vert.pos += states[i].vel * dt;
            vert.rotation += states[i].rotvel * dt;

            wrap( -400.0f, vert.pos.x, 400.0f );
            wrap( -400.0f, vert.pos.y, 400.0f );
            wrap( 0.0f, vert.rotation, 2 * 3.14159f );

            verts[i*4 + 1].pos = verts[i*4 + 2].pos = verts[i*4 + 3].pos = vert.pos;
            verts[i*4 + 1].rotation = verts[i*4 + 2].rotation = verts[i*4 + 3].rotation = vert.rotation;
        }
    }

    struct Vertex
    {
        vec2 pos;
        vec2 dim;
        vec2 meta;
        float rotation;
        unsigned char r, g, b, a;
    };

    struct State
    {
        vec2 vel;       // units per second
        float rotvel;   // radians per second
    };

    vector< Vertex > verts;
    vector< State > states;
};

// RAII vertex attribute wrapper
struct Attrib
{
    Attrib( GLint prog, const char* name, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid* pointer )
    {
        mLoc = glGetAttribLocation( prog, name );
        if( mLoc < 0 ) return;
        glVertexAttribPointer( mLoc, size, type, normalized, stride, pointer );
        glEnableVertexAttribArray( mLoc );
    }

    ~Attrib()
    {
        if( mLoc >=0 ) glDisableVertexAttribArray( mLoc );
    }

    GLint mLoc;
};

// GLSL shader program loader
struct Program
{
    static GLuint Load( const char* vert, const char* geom, const char* frag )
    {
        GLuint prog = glCreateProgram();
        if( vert ) AttachShader( prog, GL_VERTEX_SHADER, vert );
        if( geom ) AttachShader( prog, GL_GEOMETRY_SHADER, geom );
        if( frag ) AttachShader( prog, GL_FRAGMENT_SHADER, frag );
        glLinkProgram( prog );
        CheckStatus( prog );
        return prog;
    }

private:
    static void CheckStatus( GLuint obj )
    {
        GLint status = GL_FALSE, len = 10;
        if( glIsShader(obj) )   glGetShaderiv( obj, GL_COMPILE_STATUS, &status );
        if( glIsProgram(obj) )  glGetProgramiv( obj, GL_LINK_STATUS, &status );
        if( status == GL_TRUE ) return;
        if( glIsShader(obj) )   glGetShaderiv( obj, GL_INFO_LOG_LENGTH, &len );
        if( glIsProgram(obj) )  glGetProgramiv( obj, GL_INFO_LOG_LENGTH, &len );
        std::vector< char > log( len, 'X' );
        if( glIsShader(obj) )   glGetShaderInfoLog( obj, len, NULL, &log[0] );
        if( glIsProgram(obj) )  glGetProgramInfoLog( obj, len, NULL, &log[0] );
        std::cerr << &log[0] << std::endl;
        exit( -1 );
    }

    static void AttachShader( GLuint program, GLenum type, const char* src )
    {
        GLuint shader = glCreateShader( type );
        glShaderSource( shader, 1, &src, NULL );
        glCompileShader( shader );
        CheckStatus( shader );
        glAttachShader( program, shader );
        glDeleteShader( shader );
    }
};

#define GLSL(version, shader) "#version " #version "\n" #shader

const char* vert = GLSL
(
    120,
    uniform mat4 projection;
    uniform mat4 modelview;

    attribute vec2 position;
    attribute vec2 scale;
    attribute float rotation;
    attribute vec4 color;

    attribute vec2 meta;

    varying vec4 fragColor;
    varying vec2 fragTexCoord;

    void main( void )
    {
        fragColor = color;

        vec2 off;
        vec2 tex;
        // probably a better way to do this
        if( meta.x < 10.0 )
        {
            off = vec2( -1.0, -1.0 );
            tex = vec2( 0.0, 0.0 );
        }
        else if( meta.x < 20.0 )
        {
            off = vec2( 1.0, -1.0 );
            tex = vec2( 1.0, 0.0 );
        }
        else if( meta.x < 30.0 )
        {
            off = vec2( 1.0, 1.0 );
            tex = vec2( 1.0, 1.0 );
        }
        else if( meta.x < 40.0 )
        {
            off = vec2( -1.0, 1.0 );
            tex = vec2( 0.0, 1.0 );
        }
        fragTexCoord = tex;

        // column 1,
        // column 2,
        // column 3
        mat3 scale_mat = mat3
            (
            0.5*scale.x,    0.0,            0.0,
            0.0,            0.5*scale.y,    0.0,
            0.0,            0.0,            1.0
            );

        mat3 rotate_mat = mat3
            (
            cos(rotation),  sin(rotation),  0.0,
            -sin(rotation), cos(rotation),  0.0,
            0.0,            0.0,            1.0
            );

        mat3 translate_mat = mat3
            (
            1.0,        0.0,        0.0,
            0.0,        1.0,        0.0,
            position.x, position.y, 1.0
            );

        vec3 xformed = translate_mat * rotate_mat * scale_mat * vec3( off, 1.0 );

        gl_Position = projection * modelview * vec4( xformed, 1.0 );
    }
);

const char* frag = GLSL
(
    120,
    uniform sampler2D texture;

    varying vec4 fragColor;
    varying vec2 fragTexCoord;

    void main( void )
    {
        gl_FragColor = fragColor * texture2D( texture, fragTexCoord );
    }
);

GLuint tex = 0;
void display()
{
    // timekeeping
    static int prvTime = glutGet(GLUT_ELAPSED_TIME);
    const int curTime = glutGet(GLUT_ELAPSED_TIME);
    const float dt = ( curTime - prvTime ) / 1000.0f;
    prvTime = curTime;

    // sprite updates
    static SpriteWrangler wrangler( 100 );
    wrangler.Update( dt );
    vector< SpriteWrangler::Vertex >& verts = wrangler.verts;

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    // set up projection and camera
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    double w = glutGet( GLUT_WINDOW_WIDTH );
    double h = glutGet( GLUT_WINDOW_HEIGHT );
    double ar = w / h;
    glOrtho( -400 * ar, 400 * ar, -400, 400, -1, 1);

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();

    // prepare to render
    static GLuint prog = Program::Load( vert, NULL, frag );
    glUseProgram( prog );

    GLfloat projection[16];
    glGetFloatv( GL_PROJECTION_MATRIX, projection );
    glUniformMatrix4fv( glGetUniformLocation( prog, "projection" ), 1, GL_FALSE, projection );

    GLfloat modelview[16];
    glGetFloatv( GL_MODELVIEW_MATRIX, modelview );
    glUniformMatrix4fv( glGetUniformLocation( prog, "modelview" ), 1, GL_FALSE, modelview );

    glUniform1i( glGetUniformLocation( prog, "texture" ), 0 );
    glActiveTexture( GL_TEXTURE0 );
    glBindTexture( GL_TEXTURE_2D, tex );

    // render
    {
        Attrib a1( prog, "position", 2, GL_FLOAT, GL_FALSE, sizeof(SpriteWrangler::Vertex), &verts[0].pos.x );
        Attrib a2( prog, "meta", 2, GL_FLOAT, GL_FALSE, sizeof(SpriteWrangler::Vertex), &verts[0].meta.x );
        Attrib a3( prog, "scale", 2, GL_FLOAT, GL_FALSE, sizeof(SpriteWrangler::Vertex), &verts[0].dim.x );
        Attrib a4( prog, "rotation", 1, GL_FLOAT, GL_FALSE, sizeof(SpriteWrangler::Vertex), &verts[0].rotation );
        Attrib a5( prog, "color", 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(SpriteWrangler::Vertex), &verts[0].r );
        glDrawArrays( GL_QUADS, 0, verts.size() );
    }

    glutSwapBuffers();
}

// run display() every 16ms or so
void timer( int extra )
{
    glutTimerFunc( 16, timer, 0 );
    glutPostRedisplay();
}

int main(int argc, char **argv)
{
    glutInit( &argc, argv );
    glutInitWindowSize( 600, 600 );
    glutInitDisplayMode( GLUT_RGBA | GLUT_DEPTH | GLUT_DOUBLE );
    glutCreateWindow( "GLSL Sprites" );
    glewInit();

    // create random texture
    unsigned char buffer[ 32 * 32 * 3 ];
    for( unsigned int i = 0; i < sizeof( buffer ); ++i )
    {
        buffer[i] = (unsigned char)linearRand( 0.0f, 255.0f );
    }

    // upload texture data
    glGenTextures(1, &tex);
    glBindTexture(GL_TEXTURE_2D, tex);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glPixelStorei( GL_UNPACK_ALIGNMENT, 1 );
    glTexImage2D(GL_TEXTURE_2D, 0, 3, 32, 32, 0, GL_RGB, GL_UNSIGNED_BYTE, buffer);

    glutDisplayFunc( display );
    glutTimerFunc( 0, timer, 0 );
    glutMainLoop();
    return 0;
}

Stick your sprite info in a vertex attribute(s) and apply them in your vertex shader.

Alternatively you can move the scaling, translation, and rotation computation onto the CPU and just pass off the pre-transformed vertex buffer off to the GPU each frame. This will cut way down on GL API calls, at the cost of increased CPU usage. I'd recommend Eigen or glm for the heavy matrix lifting.

EDIT: Shader solution:

// g++ main.cpp -lGLEW -lglut -lGL
#include <GL/glew.h>
#include <GL/glut.h>
#include <iostream>
#include <vector>
#include <cmath>
using namespace std;

// OpenGL Mathematics (GLM): http://glm.g-truc.net/
#include <glm/glm.hpp>
#include <glm/gtc/random.hpp>
using namespace glm;

// stores/manipulates a list of rectangular sprites and their vertexes
struct SpriteWrangler
{
    SpriteWrangler( unsigned int aSpriteCount )
    {
        verts.resize( aSpriteCount * 4 );
        states.resize( aSpriteCount );

        for( size_t i = 0; i < states.size(); ++i )
        {
            states[i].vel = linearRand( vec2( -30, -30 ), vec2( 30, 30 ) );
            states[i].rotvel = linearRand( -1.0f, 1.0f );

            Vertex vert;
            vert.pos = linearRand( vec2( -400, -400 ), vec2( 400, 400 ) );
            vert.dim = linearRand( vec2( 20, 20 ), vec2( 60, 60 ) );
            vert.rotation = linearRand( 0.0f, 2 * 3.14159f );
            vert.r = (unsigned char)linearRand( 64.0f, 255.0f );
            vert.g = (unsigned char)linearRand( 64.0f, 255.0f );
            vert.b = (unsigned char)linearRand( 64.0f, 255.0f );
            vert.a = 255;

            vert.meta = vec2( 5, 0 );
            verts[i*4 + 0] = vert;
            vert.meta = vec2( 15, 0 );
            verts[i*4 + 1] = vert;
            vert.meta = vec2( 25, 0 );
            verts[i*4 + 2] = vert;
            vert.meta = vec2( 35, 0 );
            verts[i*4 + 3] = vert;
        }
    }

    void wrap( const float minVal, float& val, const float maxVal )
    {
        if( val < minVal )
            val = maxVal - fmod( maxVal - val, maxVal - minVal );
        else
            val = minVal + fmod( val - minVal, maxVal - minVal );
    }

    void Update( float dt )
    {
        for( size_t i = 0; i < states.size(); ++i )
        {
            Vertex& vert = verts[i*4 + 0];
            vert.pos += states[i].vel * dt;
            vert.rotation += states[i].rotvel * dt;

            wrap( -400.0f, vert.pos.x, 400.0f );
            wrap( -400.0f, vert.pos.y, 400.0f );
            wrap( 0.0f, vert.rotation, 2 * 3.14159f );

            verts[i*4 + 1].pos = verts[i*4 + 2].pos = verts[i*4 + 3].pos = vert.pos;
            verts[i*4 + 1].rotation = verts[i*4 + 2].rotation = verts[i*4 + 3].rotation = vert.rotation;
        }
    }

    struct Vertex
    {
        vec2 pos;
        vec2 dim;
        vec2 meta;
        float rotation;
        unsigned char r, g, b, a;
    };

    struct State
    {
        vec2 vel;       // units per second
        float rotvel;   // radians per second
    };

    vector< Vertex > verts;
    vector< State > states;
};

// RAII vertex attribute wrapper
struct Attrib
{
    Attrib( GLint prog, const char* name, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid* pointer )
    {
        mLoc = glGetAttribLocation( prog, name );
        if( mLoc < 0 ) return;
        glVertexAttribPointer( mLoc, size, type, normalized, stride, pointer );
        glEnableVertexAttribArray( mLoc );
    }

    ~Attrib()
    {
        if( mLoc >=0 ) glDisableVertexAttribArray( mLoc );
    }

    GLint mLoc;
};

// GLSL shader program loader
struct Program
{
    static GLuint Load( const char* vert, const char* geom, const char* frag )
    {
        GLuint prog = glCreateProgram();
        if( vert ) AttachShader( prog, GL_VERTEX_SHADER, vert );
        if( geom ) AttachShader( prog, GL_GEOMETRY_SHADER, geom );
        if( frag ) AttachShader( prog, GL_FRAGMENT_SHADER, frag );
        glLinkProgram( prog );
        CheckStatus( prog );
        return prog;
    }

private:
    static void CheckStatus( GLuint obj )
    {
        GLint status = GL_FALSE, len = 10;
        if( glIsShader(obj) )   glGetShaderiv( obj, GL_COMPILE_STATUS, &status );
        if( glIsProgram(obj) )  glGetProgramiv( obj, GL_LINK_STATUS, &status );
        if( status == GL_TRUE ) return;
        if( glIsShader(obj) )   glGetShaderiv( obj, GL_INFO_LOG_LENGTH, &len );
        if( glIsProgram(obj) )  glGetProgramiv( obj, GL_INFO_LOG_LENGTH, &len );
        std::vector< char > log( len, 'X' );
        if( glIsShader(obj) )   glGetShaderInfoLog( obj, len, NULL, &log[0] );
        if( glIsProgram(obj) )  glGetProgramInfoLog( obj, len, NULL, &log[0] );
        std::cerr << &log[0] << std::endl;
        exit( -1 );
    }

    static void AttachShader( GLuint program, GLenum type, const char* src )
    {
        GLuint shader = glCreateShader( type );
        glShaderSource( shader, 1, &src, NULL );
        glCompileShader( shader );
        CheckStatus( shader );
        glAttachShader( program, shader );
        glDeleteShader( shader );
    }
};

#define GLSL(version, shader) "#version " #version "\n" #shader

const char* vert = GLSL
(
    120,
    uniform mat4 projection;
    uniform mat4 modelview;

    attribute vec2 position;
    attribute vec2 scale;
    attribute float rotation;
    attribute vec4 color;

    attribute vec2 meta;

    varying vec4 fragColor;
    varying vec2 fragTexCoord;

    void main( void )
    {
        fragColor = color;

        vec2 off;
        vec2 tex;
        // probably a better way to do this
        if( meta.x < 10.0 )
        {
            off = vec2( -1.0, -1.0 );
            tex = vec2( 0.0, 0.0 );
        }
        else if( meta.x < 20.0 )
        {
            off = vec2( 1.0, -1.0 );
            tex = vec2( 1.0, 0.0 );
        }
        else if( meta.x < 30.0 )
        {
            off = vec2( 1.0, 1.0 );
            tex = vec2( 1.0, 1.0 );
        }
        else if( meta.x < 40.0 )
        {
            off = vec2( -1.0, 1.0 );
            tex = vec2( 0.0, 1.0 );
        }
        fragTexCoord = tex;

        // column 1,
        // column 2,
        // column 3
        mat3 scale_mat = mat3
            (
            0.5*scale.x,    0.0,            0.0,
            0.0,            0.5*scale.y,    0.0,
            0.0,            0.0,            1.0
            );

        mat3 rotate_mat = mat3
            (
            cos(rotation),  sin(rotation),  0.0,
            -sin(rotation), cos(rotation),  0.0,
            0.0,            0.0,            1.0
            );

        mat3 translate_mat = mat3
            (
            1.0,        0.0,        0.0,
            0.0,        1.0,        0.0,
            position.x, position.y, 1.0
            );

        vec3 xformed = translate_mat * rotate_mat * scale_mat * vec3( off, 1.0 );

        gl_Position = projection * modelview * vec4( xformed, 1.0 );
    }
);

const char* frag = GLSL
(
    120,
    uniform sampler2D texture;

    varying vec4 fragColor;
    varying vec2 fragTexCoord;

    void main( void )
    {
        gl_FragColor = fragColor * texture2D( texture, fragTexCoord );
    }
);

GLuint tex = 0;
void display()
{
    // timekeeping
    static int prvTime = glutGet(GLUT_ELAPSED_TIME);
    const int curTime = glutGet(GLUT_ELAPSED_TIME);
    const float dt = ( curTime - prvTime ) / 1000.0f;
    prvTime = curTime;

    // sprite updates
    static SpriteWrangler wrangler( 100 );
    wrangler.Update( dt );
    vector< SpriteWrangler::Vertex >& verts = wrangler.verts;

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    // set up projection and camera
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    double w = glutGet( GLUT_WINDOW_WIDTH );
    double h = glutGet( GLUT_WINDOW_HEIGHT );
    double ar = w / h;
    glOrtho( -400 * ar, 400 * ar, -400, 400, -1, 1);

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();

    // prepare to render
    static GLuint prog = Program::Load( vert, NULL, frag );
    glUseProgram( prog );

    GLfloat projection[16];
    glGetFloatv( GL_PROJECTION_MATRIX, projection );
    glUniformMatrix4fv( glGetUniformLocation( prog, "projection" ), 1, GL_FALSE, projection );

    GLfloat modelview[16];
    glGetFloatv( GL_MODELVIEW_MATRIX, modelview );
    glUniformMatrix4fv( glGetUniformLocation( prog, "modelview" ), 1, GL_FALSE, modelview );

    glUniform1i( glGetUniformLocation( prog, "texture" ), 0 );
    glActiveTexture( GL_TEXTURE0 );
    glBindTexture( GL_TEXTURE_2D, tex );

    // render
    {
        Attrib a1( prog, "position", 2, GL_FLOAT, GL_FALSE, sizeof(SpriteWrangler::Vertex), &verts[0].pos.x );
        Attrib a2( prog, "meta", 2, GL_FLOAT, GL_FALSE, sizeof(SpriteWrangler::Vertex), &verts[0].meta.x );
        Attrib a3( prog, "scale", 2, GL_FLOAT, GL_FALSE, sizeof(SpriteWrangler::Vertex), &verts[0].dim.x );
        Attrib a4( prog, "rotation", 1, GL_FLOAT, GL_FALSE, sizeof(SpriteWrangler::Vertex), &verts[0].rotation );
        Attrib a5( prog, "color", 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(SpriteWrangler::Vertex), &verts[0].r );
        glDrawArrays( GL_QUADS, 0, verts.size() );
    }

    glutSwapBuffers();
}

// run display() every 16ms or so
void timer( int extra )
{
    glutTimerFunc( 16, timer, 0 );
    glutPostRedisplay();
}

int main(int argc, char **argv)
{
    glutInit( &argc, argv );
    glutInitWindowSize( 600, 600 );
    glutInitDisplayMode( GLUT_RGBA | GLUT_DEPTH | GLUT_DOUBLE );
    glutCreateWindow( "GLSL Sprites" );
    glewInit();

    // create random texture
    unsigned char buffer[ 32 * 32 * 3 ];
    for( unsigned int i = 0; i < sizeof( buffer ); ++i )
    {
        buffer[i] = (unsigned char)linearRand( 0.0f, 255.0f );
    }

    // upload texture data
    glGenTextures(1, &tex);
    glBindTexture(GL_TEXTURE_2D, tex);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glPixelStorei( GL_UNPACK_ALIGNMENT, 1 );
    glTexImage2D(GL_TEXTURE_2D, 0, 3, 32, 32, 0, GL_RGB, GL_UNSIGNED_BYTE, buffer);

    glutDisplayFunc( display );
    glutTimerFunc( 0, timer, 0 );
    glutMainLoop();
    return 0;
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文