2012-01-03 4 views
2

私たちはここでは2Dスプライトを扱っているので、スプライトごとに4頂点(8 GLintのもの)と4つのテクスチャ(別の8 GLintのもの)があります。私は、1回のパスでレンダリングできるスプライトのリストを吐き出すソートルーチンを持っています(同じブレンディングと同じテクスチャ、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() 

さて、これはひどく速くありません。私はすべてを1回のパスで描きたいと思います。翻訳、回転などのデータをOpenGLにどのように渡すかわかりません。誰かが効率的なレンダリングパスを指摘できれば、それは非常に良いでしょう。

正確ごとのスプライト固有のデータは次のとおり

  • カラー(赤、緑、青、アルファ)
  • スケール(XおよびY因子)
  • 翻訳(xおよびy)
  • 回転
  • Texcoords(8 GLint年代)
  • 頂点(8 GLint年代)

私はOpenGLを慣れ親しんでください。

+0

あなたが見ているスプライトの数とフレーム時間のどのような種類の「これはそれほど速くありませんか」?あなたの擬似コードは、あなたがすでにテクスチャアトラスを使用していることを意味しています。 – genpfault

+0

@genpfault:nah私はちょうど同じテクスチャを持つスプライトを一緒に置くスマートソートアルゴリズムを使用します。それはそれです。 – orlp

答えて

2

頂点属性にスプライト情報を貼り付け、それらを頂点シェーダに適用します。

また、スケーリング、変換、回転の計算をCPU上に移動し、変換済みの頂点バッファをGPUに渡して各フレームに渡すこともできます。これにより、GL APIコールの処理速度が低下し、CPU使用率が増加します。重い吊り上げの場合は、Eigenまたはglmをお勧めします。

EDIT:シェーダソリューション:

// 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; 
} 
+0

シェーダでこれを行うのに便利なことがありますか? – orlp

+0

OpenGL.orgには[少なくとも1つのGLSLチュートリアル](http://www.opengl.org/sdk/docs/tutorials/ClockworkCoders/)があります。私はおそらくプログラマブルなOpenGL for "reals"を学ぶべきであり、数日でいくつかのデモコードがあるかもしれません。 – genpfault

+0

@ nightcracker:シェーダソリューションを追加しました。私があなたにリンクしているチュートリアルはかなり...悪かったです。 [これはより良いものです](http://duriansoftware.com/joe/An-intro-to-modern-OpenGL.-Table-of-Contents.html)。 – genpfault

関連する問題