OpenGL顶点缓冲区不正确的渲染
Posted
技术标签:
【中文标题】OpenGL顶点缓冲区不正确的渲染【英文标题】:OpenGL Vertex Buffer incorrect render 【发布时间】:2013-10-27 05:30:10 【问题描述】:我遇到了一个奇怪的问题。我正在尝试在我的 Windows 系统上使用 OpenGL 渲染一些数据。我在 opengl-tutorial.org 找到了一组针对 OpenGL 3.3 编写的教程。由于我的笔记本电脑(我在其中进行了大量开发)仅支持 OpenGL 2.1,因此我继续下载本教程的 OpenGL 2.1 端口。我对其进行了一些修改,添加了功能并对其进行了重构以实现可扩展性,但发现了一些奇怪的东西。每当我使用 Vertex Buffer Objects 渲染我的数据时,我得到的数据表示相当不正确。这如下所示。 http://www.majhost.com/gallery/DagonEcelstraun/Others/HelpNeeded/badrender.png 但是,当我使用 glVertex3fv 等指定我的数据时,我得到了更好的结果,如下所示。 http://www.majhost.com/gallery/DagonEcelstraun/Others/HelpNeeded/goodrender.png 这个问题出现在我的带有 Intel i3 集成显卡的 Windows 8.1 笔记本电脑和带有 nVidia GTX 660 的 Windows 7 台式机上,所以这不是硬件问题。有谁知道这里可能是什么问题?
加载网格数据:
const aiScene *scene = aiImportFile( sName.c_str(),
aiProcessPreset_TargetRealtime_MaxQuality | aiProcess_FlipUVs );
const aiMesh *mesh = scene->mMeshes[0];
for( int i = 0; i < mesh->mNumVertices; i++ )
meshData.push_back( mesh->mVertices[i][0] );
meshData.push_back( mesh->mVertices[i][1] );
meshData.push_back( mesh->mVertices[i][2] );
meshData.push_back( mesh->mNormals[i][0] );
meshData.push_back( mesh->mNormals[i][1] );
meshData.push_back( mesh->mNormals[i][2] );
meshData.push_back( mesh->mTextureCoords[0][i][0] );
meshData.push_back( mesh->mTextureCoords[0][i][1] );
meshData.push_back( 0 );
meshData.push_back( mesh->mTangents[i][0] );
meshData.push_back( mesh->mTangents[i][1] );
meshData.push_back( mesh->mTangents[i][2] );
for( int i = 0; i < mesh->mNumFaces; i++ )
for( int j = 0; j < 3; j++ )
indices.push_back( mesh->mFaces[i].mIndices[j] );
第一次向显卡发送数据(在前面的代码之后调用):
glGenBuffers( 1, &glVertData );
glBindBuffer( GL_ARRAY_BUFFER, glVertData );
glBufferData( GL_ARRAY_BUFFER, meshData.size() * sizeof( GLfloat ), &meshData[0], GL_STATIC_DRAW );
// Generate a buffer for the indices as well
glGenBuffers( 1, &glIndexes );
glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, glIndexes );
glBufferData( GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(unsigned short), &indices[0], GL_STATIC_DRAW );
渲染网格:
//Tell the shader to use our data
//bindVerts, bindUvs, bindNorms, and bindTangents refer to attribute variables in my shader
//vertexPosition_modelspace, vertexUV, vertexNormal_modelspace, and vertexTangent_modelspace, respectively.
this->verts = bindVerts;
this->uvs = bindUvs;
this->norms = bindNorms;
this->tangents = bindTangents;
glEnableVertexAttribArray( verts );
glEnableVertexAttribArray( uvs );
glEnableVertexAttribArray( norms );
glEnableVertexAttribArray( tangents );
//Specify how the graphics card should decode our data
// 1rst attribute buffer : vertices
glBindBuffer( GL_ARRAY_BUFFER, glVertData );
glVertexAttribPointer( verts, 3, GL_FLOAT, GL_FALSE, 12, (void*) 0 );
// 2nd attribute buffer : normals
glVertexAttribPointer( norms, 3, GL_FLOAT, GL_FALSE, 12, (void*) 3 );
//3rd attribute buffer : UVs
glVertexAttribPointer( uvs, 3, GL_FLOAT, GL_FALSE, 12, (void*) 6 );
//4th attribute buffer: tangents
glVertexAttribPointer( tangents, 3, GL_FLOAT, GL_FALSE, 12, (void*) 9 );
// Index buffer
glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, glIndexes );
//rendering the mesh with VBOs:
glDrawElements( GL_LINES, indices.size(), GL_UNSIGNED_SHORT, (void*) 0 );
//specifying the vertex data individually:
glBegin( GL_TRIANGLES );
int ind;
for( int i = 0; i < indices.size(); i++ )
ind = indices[i] * 12;
glNormal3fv( &meshData[ind + 3] );
glTexCoord2fv( &meshData[ind + 6] );
glVertex3fv( &meshData[ind] );
glEnd();
//clean up after the render
glDisableVertexAttribArray( verts );
glDisableVertexAttribArray( uvs );
glDisableVertexAttribArray( norms );
glDisableVertexAttribArray( tangents );
我的顶点着色器:
#version 130
// Input vertex data, different for all executions of this shader.
//it doesn't work, so we'll just get rid of it
attribute vec3 vertexPosition_modelspace;
attribute vec3 vertexUV;
attribute vec3 vertexNormal_modelspace;
attribute vec3 vertexTangent_modelspace;
// Output data ; will be interpolated for each fragment.
out vec2 UV;
out vec3 Position_worldspace;
out vec3 Normal_cameraspace;
out vec3 EyeDirection_cameraspace;
out vec3 LightDirection_cameraspace;
out vec4 ShadowCoord;
// Values that stay constant for the whole mesh.
uniform mat4 MVP;
uniform mat4 V;
uniform mat4 M;
uniform vec3 LightInvDirection_worldspace;
uniform mat4 DepthBiasMVP;
uniform sampler2D normalMap;
attribute vec3 vTangent;
void main()
// Output position of the vertex, in clip space : MVP * position
gl_Position = MVP * vec4( vertexPosition_modelspace, 1 );
ShadowCoord = DepthBiasMVP * vec4( vertexPosition_modelspace, 0 );
// Position of the vertex, in worldspace : M * position
Position_worldspace = ( M * vec4( vertexPosition_modelspace, 0 ) ).xyz;
// Vector that goes from the vertex to the camera, in camera space.
// In camera space, the camera is at the origin (0,0,0).
EyeDirection_cameraspace = vec3( 0, 0, 0 ) - ( V * M * vec4( vertexPosition_modelspace, 0 ) ).xyz;
// Vector that goes from the vertex to the light, in camera space
LightDirection_cameraspace = ( V * vec4( LightInvDirection_worldspace, 0 ) ).xyz;
// UV of the vertex. No special space for this one.
UV = vertexUV.st;
// Normal of the the vertex, in camera space
// Only correct if ModelMatrix does not scale the model ! Use its inverse transpose if not.
Normal_cameraspace = ( V * M * vec4( vertexNormal_modelspace.xyz, 0 ) ).xyz;
片段着色器:
#version 130
// Interpolated values from the vertex shaders
in vec2 UV;
in vec3 Position_worldspace;
in vec3 Normal_cameraspace;
in vec3 EyeDirection_cameraspace;
in vec3 LightDirection_cameraspace;
in vec4 ShadowCoord;
out vec4 fragColor;
// Values that stay constant for the whole mesh.
uniform sampler2D diffuse;
uniform mat4 MV;
uniform vec3 LightPosition_worldspace;
uniform sampler2D shadowMap;
//uniform int shadowLevel; //0 is no shadow, 1 is hard shadows, 2 is soft shadows, 3 is PCSS
// Returns a random number based on a vec3 and an int.
float random( vec3 seed, int i )
vec4 seed4 = vec4( seed, i );
float dot_product = dot( seed4, vec4( 12.9898, 78.233, 45.164, 94.673 ) );
return fract( sin( dot_product ) * 43758.5453 );
int mod( int a, int b )
return a - (a / b);
void main()
int shadowLevel = 1; //let's just do hard shadows
// Light emission properties
vec3 LightColor = vec3( 1, 1, 1 );
float LightPower = 1.0f;
// Material properties
vec3 MaterialDiffuseColor = texture( diffuse, UV ).rgb;
vec3 MaterialAmbientColor = vec3( 0.1, 0.1, 0.1 ) * MaterialDiffuseColor;
vec3 MaterialSpecularColor = vec3( 0.3, 0.3, 0.3 );
vec3 n = normalize( Normal_cameraspace );
vec3 l = normalize( LightDirection_cameraspace );
float cosTheta = clamp( dot( n, l ), 0.2, 1 );
// Eye vector (towards the camera)
vec3 E = normalize( EyeDirection_cameraspace );
// Direction in which the triangle reflects the light
vec3 R = reflect( -l, n );
// Cosine of the angle between the Eye vector and the Reflect vector,
// clamped to 0
// - Looking into the reflection -> 1
// - Looking elsewhere -> < 1
float cosAlpha = clamp( dot( E, R ), 0, 1 );
float visibility = 1.0;
//variable bias
float bias = 0.005 * tan( acos( cosTheta ) );
bias = clamp( bias, 0, 0.01 );
// dFragment to the light
float dFragment = ( ShadowCoord.z-bias ) / ShadowCoord.w;
float dBlocker = 0;
float penumbra = 1;
float wLight = 5.0;
if( shadowLevel == 3 )
// Sample the shadow map 8 times
float count = 0;
float temp;
float centerBlocker = texture( shadowMap, ShadowCoord.xy).r;
float scale = (wLight * (dFragment - centerBlocker)) / dFragment;
for( int i = 0; i < 16; i++ )
temp = texture( shadowMap, ShadowCoord.xy + (scale * poissonDisk( i ) / 50.0) ).r;
if( temp < dFragment )
dBlocker += temp;
count += 1;
if( count > 0 )
dBlocker /= count;
penumbra = wLight * (dFragment - dBlocker) / dFragment;
if( shadowLevel == 1 )
if( texture( shadowMap, ShadowCoord.xy).r < dFragment )
visibility -= 0.8;
else if( shadowLevel > 1 )
float iterations = 32;
float sub = 0.8f / iterations;
for( int i = 0; i < iterations; i++ )
int index = mod( int( 32.0 * random( gl_FragCoord.xyy, i ) ), 32 );
if( texture( shadowMap, ShadowCoord.xy + (penumbra * poissonDisk( index ) / 250.0) ).r < dFragment )
visibility -= sub;
visibility = min( visibility, cosTheta );
//MaterialDiffuseColor = vec3( 0.8, 0.8, 0.8 );
fragColor.rgb = MaterialAmbientColor +
visibility * MaterialDiffuseColor * LightColor * LightPower +
visibility * MaterialSpecularColor * LightColor * LightPower * pow( cosAlpha, 5 );
请注意, poissonDisk( int ind ) 返回一个 vec2,其幅度不超过 1,属于泊松磁盘分布。尽管我使用的是着色器版本 130,但我使用的是函数而不是数组,因为数组在我的笔记本电脑上运行得相当慢。
在进行任何渲染之前,我会绑定该着色器。我还确保将正确的变量上传到我的所有制服,但我没有显示以节省空间,因为我知道它工作正常。
有谁知道是什么导致了这个不正确的渲染?
【问题讨论】:
【参考方案1】:首先,停止使用GL_LINES
绘制VBO。立即模式和 VBO 绘图使用相同的原始模式。
另外,从什么时候开始 3*4 = 3?当使用交错数据结构时,VBO 顶点指针中的地址(偏移量)应该是元素数量乘以数据类型的大小。 GL_FLOAT
是 4 个字节,如果您有一个 3 分量的顶点位置,这意味着您的 VBO 中下一个字段的偏移量是 3*4 = (void *)12
,而不是 (void *)3
。对于每个额外的顶点数组指针,这个过程必须继续,它们都使用了不正确的偏移量。
同样,VBO 的步幅应该是 12 * sizeof (GLfloat) = 48,而不是 12。
【讨论】:
我这样做了,并从 nvoglv32.dll 中得到了一些错误,一些快速的谷歌搜索显示为我的 nVidia 卡的 OpenGL 驱动程序。具体来说,它告诉我从位置 0x00000000 读取时遇到问题。当我调用 glDrawElements 时,一些快速调试发现问题出在 Mesh::draw() 中。我还发现我正在尝试将顶点位置、法线、切线和 UV 发送到位置 0。一旦我解决了这个问题,它就非常有效。谢谢!以上是关于OpenGL顶点缓冲区不正确的渲染的主要内容,如果未能解决你的问题,请参考以下文章