使用OpenGL 4.0 +,我很难在项目中使用UBO支持实现轻量数据。
我正在尝试使用UBO存储预定义最大数量的光数据数组,并从片段着色器访问它。
这是着色器:
#version 400 core
in vec2 tCoord;
smooth in vec4 vPosition;
smooth in vec4 vNormal;
// Diffuse texture.
uniform sampler2D sTexture0;
uniform vec4 vMatAmbientColor;
uniform vec4 vMatDiffuseColor;
uniform vec4 vMatSpecularColor;
out vec4 fragColor;
struct LightData
{
int Type;
vec3 Position;
vec3 Direction;
float Radius;
float Attenuation;
float CutOffAngle;
vec4 AmbientColor;
vec4 DiffuseColor;
vec4 SpecularColor;
};
#define MAX_LIGHTS_COUNT 32
layout ( std140 ) uniform LightsList
{
LightData Light[MAX_LIGHTS_COUNT];
int LightsCount;
} Lights;
vec4 calculateLighting( LightData _light )
{
vec4 _result = vec4( 0.0 );
if( _light.Type == 1 ) // directional
{
_result = ( _light.AmbientColor * vMatAmbientColor ) + ( _light.DiffuseColor * vMatDiffuseColor * vec4( texture( sTexture0, tCoord ).rgba ) ) + ( _light.SpecularColor * vMatSpecularColor );
}
else
if( _light.Type == 2 ) // point
{
float _attenuation = 1.0 / ( 1.0 + _light.Attenuation * pow( distance( vec4( _light.Position.xyz, 1.0 ), vPosition ), 2 ) );
_result = ( _light.AmbientColor * vMatAmbientColor ) + ( ( _light.DiffuseColor * vMatDiffuseColor * vec4( texture( sTexture0, tCoord ).rgba ) ) + ( _light.SpecularColor * vMatSpecularColor ) ) * _attenuation;
}
else
if( _light.Type == 3 ) // spot
{
float _attenuation = 1.0 / ( 1.0 + _light.Attenuation * pow( distance( vec4( _light.Position.xyz, 1.0 ), vPosition ), 2 ) );
_result = ( _light.AmbientColor * vMatAmbientColor ) + ( ( _light.DiffuseColor + vMatDiffuseColor * texture( sTexture0, tCoord ) ) + ( _light.SpecularColor * vMatSpecularColor ) ) * _attenuation;
}
return _result;
}
void main( void )
{
fragColor = vec4( Lights.LightsCount );
int i;
for( i = 0; i < Lights.LightsCount; ++i )
{
fragColor += calculateLighting( Lights.Light[ i ] );
}
}
注意:没关系在calculateLighting()中的代码,现在它是一个虚拟代码,但它应该提供至少任何计算和返回的结果的最小结果。
在C ++中,我定义了这样的字段名称:
const GLchar* k_sLightsListMemberNames[ 2 ] =
{
"LightsList.Light[0].Type",
"LightsList.LightsCount"
};
const GLchar* k_sLightDataMemberNames[ 9 ] =
{
"LightsList.Light[0].Type",
"LightsList.Light[0].Position",
"LightsList.Light[0].Direction",
"LightsList.Light[0].Radius",
"LightsList.Light[0].Attenuation",
"LightsList.Light[0].CutOffAngle",
"LightsList.Light[0].AmbientColor",
"LightsList.Light[0].DiffuseColor",
"LightsList.Light[0].SpecularColor"
};
C ++,设置代码:
// Initialize light data storage.
if( m_iProgram && ( s_iLightDataBufferID == 0U ) )
{
s_iLightDataBlockIndex = glGetUniformBlockIndex( m_iProgram, "LightsList" );
glGetActiveUniformBlockiv( m_iProgram, s_iLightDataBlockIndex, GL_UNIFORM_BLOCK_DATA_SIZE, &s_iLightDataBufferSize );
// Get indices for light data.
glGetUniformIndices(
m_iProgram,
k_iLightsListMembersCount,
k_sLightsListMemberNames,
s_iLightsListMemberIndices
);
glGetUniformIndices(
m_iProgram,
k_iLightDataMembersCount,
k_sLightDataMemberNames,
s_iLightDataMemberIndices
);
if( s_iLightsListMemberIndices[ 0 ] != GL_INVALID_INDEX )
{
// Get offsets for members of LightsList.
glGetActiveUniformsiv(
m_iProgram,
k_iLightsListMembersCount,
s_iLightsListMemberIndices,
GL_UNIFORM_OFFSET,
s_iLightsListMemberOffsets
);
const GLchar* _lightsList2ndElementName[ 1 ] = { "LightsList.Light[1].Type" };
GLuint _lightsList2ndElementIndex[ 1 ] = { GL_INVALID_INDEX };
GLint _lightsList2ndElementOffset[ 1 ] = { 0 };
glGetUniformIndices( m_iProgram, 1, _lightsList2ndElementName, _lightsList2ndElementIndex );
glGetActiveUniformsiv( m_iProgram, 1, _lightsList2ndElementIndex, GL_UNIFORM_OFFSET, _lightsList2ndElementOffset );
s_iLightsListArrayStride = _lightsList2ndElementOffset[ 0 ] - s_iLightsListMemberOffsets[ 0 ];
glGetActiveUniformsiv(
m_iProgram,
k_iLightDataMembersCount,
s_iLightDataMemberIndices,
GL_UNIFORM_OFFSET,
s_iLightDataMemberOffsets
);
// Create UBO.
glGenBuffers( 1, &s_iLightDataBufferID );
glBindBuffer( GL_UNIFORM_BUFFER, s_iLightDataBufferID );
glBufferData( GL_UNIFORM_BUFFER, s_iLightDataBufferSize, nullptr, GL_DYNAMIC_DRAW );
glUniformBlockBinding( m_iProgram, s_iLightDataBlockIndex, k_iLightDataUniformBlockBinding );
// Bind data buffer to blocks.
glBindBufferBase( GL_UNIFORM_BUFFER, k_iLightDataUniformBlockBinding, s_iLightDataBufferID );
// Cleanup.
glBindBuffer( GL_UNIFORM_BUFFER, 0 );
}
}
注意2:我计算数组步幅的方式只是暂时的,这只是因为现在我无法获得glGetActiveUniformsiv()来正确返回Light []数组的数组步幅。现在它并不重要。
C ++,每帧缓冲区更新:
if( s_iLightDataBufferID != 0U )
{
// Establish binding and mapping.
glBindBuffer( GL_UNIFORM_BUFFER, s_iLightDataBufferID );
void* _map = glMapBuffer( GL_UNIFORM_BUFFER, GL_WRITE_ONLY );
// Calculate and store lights count.
unsigned int _lightsCount = 0;
if( _maxCount == -1 )
{
_lightsCount = _lights.size();
}
else
{
_lightsCount = ( unsigned int )( _maxCount );
}
if( _lightsCount > k_iMaxAllowedLightSources )
{
_lightsCount = k_iMaxAllowedLightSources;
}
memcpy( _map + s_iLightsListMemberOffsets[ 1 ], &( _lightsCount ), sizeof( GLint ) );
// Assemble lights data.
for( unsigned int _lightIndex = 0; _lightIndex < _lightsCount; ++_lightIndex )
{
GLint _baseOffset = _lightIndex * s_iLightsListArrayStride;
gizmo::scene::CGLightSceneObject* _nextLight = _lights[ _lightIndex ];
// Type.
GLint _type = _nextLight->getLightType();
memcpy( _map + _baseOffset + s_iLightDataMemberOffsets[ 0 ], &( _type ), sizeof( GLint ) );
// Position.
irr::core::vector3df _position = _nextLight->getAbsolutePosition();
memcpy( _map + _baseOffset + s_iLightDataMemberOffsets[ 1 ], &( _position.X ), sizeof( irr::core::vector3df ) );
// Direction.
irr::core::vector3df _direction = _nextLight->getAbsoluteRotation().rotationToDirection();
memcpy( _map + _baseOffset + s_iLightDataMemberOffsets[ 2 ], &( _direction.X ), sizeof( irr::core::vector3df ) );
// Radius.
GLfloat _radius = _nextLight->getAbsoluteScale().X;
memcpy( _map + _baseOffset + s_iLightDataMemberOffsets[ 3 ], &( _radius ), sizeof( GLfloat ) );
// Attenuation.
GLfloat _attenuation = _nextLight->getAttenuation();
memcpy( _map + _baseOffset + s_iLightDataMemberOffsets[ 4 ], &( _attenuation ), sizeof( GLfloat ) );
// Cut-off angle.
GLfloat _cutOffAngle = irr::core::degToRad( _nextLight->getCutOffAngle() );
memcpy( _map + _baseOffset + s_iLightDataMemberOffsets[ 5 ], &( _cutOffAngle ), sizeof( GLfloat ) );
// Ambient color.
GLfloat _ambientColor[ 4 ];
_ambientColor[ 0 ] = _nextLight->getAmbientColor().getRed() / 255.0f;
_ambientColor[ 1 ] = _nextLight->getAmbientColor().getGreen() / 255.0f;
_ambientColor[ 2 ] = _nextLight->getAmbientColor().getBlue() / 255.0f;
_ambientColor[ 3 ] = _nextLight->getAmbientColor().getAlpha() / 255.0f;
memcpy( _map + _baseOffset + s_iLightDataMemberOffsets[ 6 ], &( _ambientColor ), 4 * sizeof( GLfloat ) );
// Diffuse color.
GLfloat _diffuseColor[ 4 ];
_diffuseColor[ 0 ] = _nextLight->getDiffuseColor().getRed() / 255.0f;
_diffuseColor[ 1 ] = _nextLight->getDiffuseColor().getGreen() / 255.0f;
_diffuseColor[ 2 ] = _nextLight->getDiffuseColor().getBlue() / 255.0f;
_diffuseColor[ 3 ] = _nextLight->getDiffuseColor().getAlpha() / 255.0f;
memcpy( _map + _baseOffset + s_iLightDataMemberOffsets[ 7 ], &( _diffuseColor ), 4 * sizeof( GLfloat ) );
// Specular color.
GLfloat _specularColor[ 4 ];
_specularColor[ 0 ] = _nextLight->getSpecularColor().getRed() / 255.0f;
_specularColor[ 1 ] = _nextLight->getSpecularColor().getGreen() / 255.0f;
_specularColor[ 2 ] = _nextLight->getSpecularColor().getBlue() / 255.0f;
_specularColor[ 3 ] = _nextLight->getSpecularColor().getAlpha() / 255.0f;
memcpy( _map + _baseOffset + s_iLightDataMemberOffsets[ 8 ], &( _specularColor ), 4 * sizeof( GLfloat ) );
}
// Clear binding.
glUnmapBuffer( GL_UNIFORM_BUFFER );
glBindBuffer( GL_UNIFORM_BUFFER, 0U );
}
在渲染过程中,我只是将VAO与相关的顶点和索引缓冲区绑定,并在GL_TRIANGLE_STRIP模式下使用glDrawElements()简单地绘制几何体。
检索到的块索引和偏移看起来像OK:
s_iLightsListMemberIndices[] = {8, 288};
s_iLightsListMemberOffsets[] = {0, 3584};
s_iLightDataMemberIndices[] = {8, 5, 4, 6, 1, 2, 0, 3, 7};
s_iLightDataMemberOffsets[] = {0, 16, 32, 44, 48, 52, 64, 80, 96};
没有光照,场景看起来很好(片段着色器中的简单纹理()获取)。但是,在场景中添加了1个灯光,并且使用了此着色器,结果为黑屏。 此外,我已经在着色器中验证Lights.LightsCount等于0(看起来像是非初始化的)。