用OpenGL绘制多纹理?
Posted
技术标签:
【中文标题】用OpenGL绘制多纹理?【英文标题】:Multi-texture drawing with OpenGL? 【发布时间】:2018-02-12 08:53:58 【问题描述】:我正在尝试使用 OpenGL 绘制一些具有 2 个或更多不同纹理的对象。例如,来自 3 个不同 spritesheets 的 3 个 sprite。 你可以看到 here 我的期望和我拥有的 我现在遇到的问题是所有精灵都来自 1 spritesheet。 让我们看看,首先在初始化时我加载了 3 个图像 img0,img1,img2
img0 = loadSurface2D("image0.png");
img1 = loadSurface2D("image1.png");
img2 = loadSurface2D("image2.png");
然后在我的绘图循环中有点像 SDL2:
BKP_Rec dest,src;
g2D_renderSurface(img0,NULL,NULL);//will draw all the sprite sheet a (0,0)
src.x = 0;
src.y = 4;
src.w = 64;
src.h = 64;
dest.x = 512;
dest.y = 300;
dest.w = 128;
dest.h = 128;
g2D_renderSurface(img1,&dest,&src);//draw sprite(0,4)
src.x = 1;
src.y = 2;
dest.x = 562;
dest.y = 300;
g2D_renderSurface(img2,&dest,&src);//draw img 2 sprite(1,2)
让我们看看 loadSurface2D
Surface2D * loadSurface2D(const char * file)
unsigned int width, height;
unsigned char * image = image_loadPNG(file,&width,&height);
//...all test & flip image are done here, removed it for lisibility
Surface2D * s = NULL;
ALLOC_L(Surface2D, s, 1);
s->data = image;
s->point[0] = 0; s->point[1] = 0 ; s->point[2] = 0; s->point[3] = 1;
s->point[4] = 0; s->point[5] = -nh; s->point[6] = 0; s->point[7] = 1;
s->point[8] = nw; s->point[9] = -nh ; s->point[10] = 0; s->point[11] = 1;
s->point[12] = nw; s->point[13] = 0 ; s->point[14] = 0; s->point[15] = 1;
s->texture[0] = 0.0f; s->texture[1] = 1.0f ;
s->texture[2] = 0.0f; s->texture[3] = 0.0f;
s->texture[4] = 1.0f; s->texture[5] = 0.0f ;
s->texture[6] = 1.0f; s->texture[7] = 1.0f ;
s->w = (GLfloat) width;
s->h = (GLfloat) height;
bkp_numcal_setIdentity4(s->Mtx_translate);
bkp_numcal_setIdentity4(s->Mtx_scale);
bkp_numcal_setIdentity4(s->Mtx_rotate);
bkp_numcal_setIdentity4(s->Mtx_i);
bkp_numcal_setIdentity4(s->Mtx);
GLuint point_vbo = 0;
glGenBuffers(1,&point_vbo);
glBindBuffer(GL_ARRAY_BUFFER, point_vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(s->point),s->point,GL_STATIC_DRAW);
GLuint tex_vbo = 1;
glGenBuffers(1,&tex_vbo);
glBindBuffer(GL_ARRAY_BUFFER, tex_vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(s->texture),s->texture,GL_STATIC_DRAW);
GLuint vao = stc_2d->vao_num;
stc_2d->vao_num += 1;
glGenVertexArrays(1,&vao);
glBindVertexArray(vao);
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, point_vbo);
glVertexAttribPointer(0,4,GL_FLOAT,GL_FALSE,0,NULL);
GLuint dimension = 2;
glEnableVertexAttribArray(1);
glBindBuffer(GL_ARRAY_BUFFER, tex_vbo);
glVertexAttribPointer(1,dimension,GL_FLOAT,GL_FALSE,0,NULL);
s->sampler_loc = glGetUniformLocation(stc_2d->sh_sprite, "basic_texture");
s->texunit_id = GL_TEXTURE0 + stc_2d->texunit_id;
stc_2d->texunit_id += 1; // initialized at 0, every function call it increments
GLuint tex = stc_2d->tex_num;
++stc_2d->tex_num; // same here, another global at 0 when initialized
glGenTextures(1, &tex);
glBindTexture(GL_TEXTURE_2D, tex);
glUseProgram(stc_2d->sprite); // EDITED
glUniform1i(s->sampler_loc, s->texunit_id);
glTexImage2D(GL_TEXTURE_2D, 0,
GL_RGBA, width, height, 0, GL_RGBA,
GL_UNSIGNED_BYTE, image);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
int st_offset_loc = glGetUniformLocation(stc_2d->sh_sprite, "vt_");
glUniformMatrix2fv(st_offset_loc, 1,GL_FALSE, s->vt_);
s->st_offset = st_offset_loc;
s->vt_[0] = 0.0;
s->vt_[1] = 0.0;
s->vt_[2] = 0.0;
s->vt_[3] = 0.0;
s->vao = vao;
s->texid = tex;
s->tex_vbo = tex_vbo;
s->point_vbo = point_vbo;
return s;
基本上我只是加载 png 创建纹理。有一些值(stc_2D->*)在启动时初始化为 0,在计数器 ex: stc_2D->vba_num 告诉当前要创建的 vba 编号。
现在要渲染的函数
void g2D_renderSurface(BKP_Surface2D * s,BKP_Rec * dest,BKP_Rec * src)
glUseProgram(stc_2d->sh_sprite);
glBindVertexArray(s->vao);
glActiveTexture(s->texunit_id);
glBindTexture(GL_TEXTURE_2D, s->texid);
//glUniform1i(s->sampler_loc, s->texunit_id);
假设我没有评论这一行
float xf,yf;
if(dest != NULL)
xf = (dest->x / (float) stc_fb_width * 2 - 1) ;
yf = - ((dest->y / (float) stc_fb_height * 2 - 1));
bkp_numcal_setFreeScalMatrix4(s->Mtx_scale,dest->w /s->w, dest->h/ s->h, 1.0f);
else
xf = - 1 ;
yf = 1;
bkp_numcal_setIdentity4(s->Mtx_scale);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
if(src != NULL)
s->vt_[0] =(float)(src->x * src->w) / s->w;
s->vt_[1] = (float) src->w / s->w;
s->vt_[2] =(s->h - (float)((1 + src->y) * src->h) )/ s->h;
s->vt_[3] = (float) src->h / s->h;
else
s->vt_[0] = 0.0;
s->vt_[1] = (float) s->w / s->w;
s->vt_[2] = 0.0;
s->vt_[3] = (float) s->h / s->h;
glUniform4fv(s->st_offset,1,s->vt_);
bkp_numcal_translate4(s->Mtx_translate, xf, yf, 0);
bkp_numcal_multMat4Mat4(s->Mtx,s->Mtx_scale,s->Mtx_translate);
glUniformMatrix4fv(stc_2d->mtx_location, 1,GL_FALSE, s->Mtx);
return;
现在是着色器。
顶点:
#version 410\n
in vec3 vertex_position;uniform mat4 matrix;
in vec2 vt_loc;
varying vec2 v_uv;
uniform vec4 vt_;
void main()
v_uv.x = (vt_loc.x * vt_[1]) + vt_[0];
v_uv.y = (vt_loc.y * vt_[3]) + (vt_[2]);
gl_Position = matrix * vec4(vertex_position, 1.0);
片段:
#version 410
out vec4 frag_colour;
uniform sampler2D basic_texture;
varying vec2 v_uv;
void main()
vec4 texel = texture(basic_texture, v_uv);
frag_colour = texel;
我认为它们可能是我不理解的东西,因为我不明白为什么它会绘制 3 个我想要的大小、我想要的位置但只有相同纹理的对象。我在想我覆盖了相同的纹理,但它是第一个显示的纹理,而不是最后一个加载的纹理。
【问题讨论】:
采样器制服basic_texture
的值必须是 0, 1, 2, ... 并且 not 'GL_TEXTURE0
, GL_TEXTURE1
, GL_TEXTURE2
, .... 看我的回答
人们知道他们服从。所以现在它可以工作了。我还有一些其他问题,因为它确实显示了 3 张图像,但纹理不是很好的对象,但基本上我有我正在寻找的答案。谢谢。
为了记录,这是我改变的。在载荷面上。 S->texunit_id = stc_2D->texunit_id。并按照建议进行渲染 glActivateTexture(GL_TEXTURE0 + s->texunit_id); glUniform1i(s->sampler_loc, s->texunit_id)
【参考方案1】:
glUniform*
指定 当前 程序对象的统一变量的值。glUseProgram
安装程序对象作为当前渲染状态的一部分。
存储由glUniform*
设置的最后一个值。从loadSurface2D
中删除所有统一值设置。在glUseProgram(stc_2d->sh_sprite);
之后立即在g2D_renderSurface
中进行统一值的所有设置。
另外注意,你必须分配给纹理采样器统一basic_texture
的值是,不是纹理单元枚举常量(不是GL_TEXTURE0
,GL_TEXTURE1
,. ..)。
它必须是纹理单元的编号(0, 1, 2. ...)。
这是错误的:
s->texunit_id = GL_TEXTURE0 + stc_2d->texunit_id;
glUniform1i(s->sampler_loc, s->texunit_id);
必须是:
s->texunit_id = stc_2d->texunit_id;
.....
glUseProgram(stc_2d->sh_sprite);
glActiveTexture(s->texunit_id + GL_TEXTURE0); // GL_TEXTURE0, GL_TEXTURE1, GL_TEXTURE2, ....
glBindTexture(GL_TEXTURE_2D, s->texid);
glUniform1i(s->sampler_loc, s->texunit_id); // 0, 1, 2, ....
【讨论】:
是的,但这不是问题,事实上我有 glUseProgram(stc_2d->sprite);我没有正确复制,让我编辑它以免分散人们的注意力。 你在loadSurface2D
之前打电话给glUseProgram(stc_2d->sprite);
?
不是之前,在 loadSurface2D 中,就在 glBindTexture() 之后【参考方案2】:
着色器中的制服始终存储它们设置的最后一个状态。由于您在绘制之前更改了活动纹理,因此您还必须更新采样器制服(该行已经存在,不知道为什么它是注释)。
glActiveTexture(s->texunit_id);
glBindTexture(GL_TEXTURE_2D, s->texid);
glUniform1i(s->sampler_loc, s->texunit_id); //<-- This is important
【讨论】:
我评论了它,因为它没有改变任何东西,我只是在尝试没有它。 我对其进行了编辑,但保留了 cmets,因为您将其标记为重要。我评论它只是为了尝试没有它会发生什么。但什么都没有改变。在我的代码中,我将取消注释。但请在此处为其他读者保留这样的内容以上是关于用OpenGL绘制多纹理?的主要内容,如果未能解决你的问题,请参考以下文章