OpenGL着色器存储缓冲区包含不完整的数据

时间:2014-08-04 17:11:10

标签: opengl visual-c++ shader

在基本灯光上阅读this tutorial后,我决定扩展着色器代码以处理任意数量的灯光。根据我的谷歌搜索结果,推荐的方法是创建一个着色器存储缓冲区对象(SSBO)和一个Lamp结构数组缓冲区来表示灯光:

// all vec3 objects are from glm
struct Lamp {
    vec3 pos;
    float power;
    vec3 color;
    vec3 lightDirectionCameraSpace;
    // this constructor is omitted in the struct definition in the shaders
    Lamp(vec3 a, float b, vec3 c) {
        pos = a;
        power = b;
        color = c;
    }
};

// global variable
vector<Lamp> lights;

Lamp lmp1(vec3(0.0f, 0.0f, 10.0f), 40.0f, vec3(0.3f, 0.7f, 0.9f));
Lamp lmp2(vec3(0.0f, 0.0f, -10.0f), 40.0f, vec3(1.0f, 0.7f, 0.7f));
lights.push_back(lmp1);
lights.push_back(lmp2);

GLuint LightID;
glGenBuffers(1, &LightID);

然后,每个渲染过程,我将灯缓冲到着色器中定义的SSBO(见下文):

glBindBuffer(GL_SHADER_STORAGE_BUFFER, LightID);
glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(Lamp)*lights.size(), &(lights[0]), GL_STATIC_DRAW);

// using binding 0 for the SSBO
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, LightID);

相关的顶点着色器代码:

#version 430

...

struct Lamp {
    vec3 pos;
    float power;
    vec3 color;
    vec3 lightDirectionCameraSpace;
};

layout(std430, binding = 0) buffer LightArray {
    Lamp[] lights;
} LightArr;

void main() {

...

    for (int i = 0; i < LightArr.lights.length(); i++) {
        // Vector that goes from the vertex to the light, in camera space. M is ommited because it's identity.
        vec3 LightPosition_cameraspace = ( V * vec4(LightArr.lights[i].pos, 1)).xyz;
        LightArr.lights[i].lightDirectionCameraSpace = LightPosition_cameraspace + EyeDirection_cameraspace;
    }

...

相关片段着色器代码:

#version 430 core

...

// Ouput data
out vec3 glcolor;

struct Lamp {
    vec3 pos;
    float power;
    vec3 color;
    vec3 lightDirectionCameraSpace;
};

layout(std430, binding = 0) buffer LightArray {
    Lamp[] lights;
} LightArr;

void main(){

    // Material properties
    vec3 MaterialDiffuseColor = texture2D( myTextureSampler, UV ).rgb;
    vec3 MaterialAmbientColor = vec3(0.1,0.1,0.1) * MaterialDiffuseColor;
    vec3 MaterialSpecularColor = vec3(0.3,0.3,0.3);

    glcolor = vec3(0, 0, 0);

    for (int i = 0; i < LightArr.lights.length(); i++) {
        // Distance to the light
        float distance = length( LightArr.lights[i].pos - Position_worldspace );

        // Normal of the computed fragment, in camera space
        vec3 n = normalize( Normal_cameraspace );
        // Direction of the light (from the fragment to the light)
        vec3 l = normalize( LightArr.lights[i].lightDirectionCameraSpace );
        // Cosine of the angle between the normal and the light direction, 
        // clamped above 0
        //  - light is at the vertical of the triangle -> 1
        //  - light is perpendicular to the triangle -> 0
        //  - light is behind the triangle -> 0
        float cosTheta = clamp( dot( n,l ), 0,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 );

        glcolor +=
            // Ambient : simulates indirect lighting
            // MaterialAmbientColor +
            // Diffuse : "color" of the object
            MaterialDiffuseColor * LightArr.lights[i].color * LightArr.lights[i].power * cosTheta / (distance*distance) +
            // Specular : reflective highlight, like a mirror
            MaterialSpecularColor * LightArr.lights[i].color * LightArr.lights[i].power * pow(cosAlpha,5) / (distance*distance);
    }

    // compute total light, then add ambient
    // Ambient : simulates indirect lighting
    glcolor = MaterialAmbientColor + glcolor;
}

问题在于,当程序运行时,它只会将第一个灯(这里是蓝色灯)添加到列表中(但显示它非常正确)。我做了一些调试,发现LightArr.lights.length() == 1无论我有多少灯。可能与此相关的是在我只使用一盏灯的情况下从一个宽的法线角度照亮的表面上的一些闪烁。

编辑:我设法解决了一些问题,但我不知道为什么我的解决方案有效。我在float foo;float bar;之后将未使用的虚拟浮点变量(Lampvec3 color;)添加到我的vec3 lightDirectionCameraSpace;结构(在着色器内部和外部)。现在,两个灯都会显示出来,但是当没有理由时,闪烁仍然存在。为什么添加垃圾变量会改变着色器呈现的灯数?

1 个答案:

答案 0 :(得分:2)

您的解决方案工作的原因是OpenGL结构包装规则。 OpenGL期望vec3在vec4边界上对齐。有关OpenGL如何在统一块中布局结构的详细信息,请参阅OpenGL 4.3规范的7.6.2节和7.6.2.2节。