简体   繁体   English

OpenGL - 制作一个跟随相机的点光源

[英]OpenGL - Making a point light that follows the camera

I'm currently creating a 3D scene, and I was wondering what would be the best way to go about creating a point light that follows the camera.我目前正在创建一个 3D 场景,我想知道创建跟随相机的点光源的最佳方法是什么。 So far my code is as follows:到目前为止,我的代码如下:

Vertex shader:顶点着色器:

// Materials
uniform vec3 materialAmbient;
uniform vec3 materialDiffuse;
uniform vec3 materialSpecular;
uniform float materialShininess;

uniform float att_quadratic = 0.1;

// Lights
struct AMBIENT
{   
    vec3 color;
};
struct DIRECTIONAL
{   
    vec3 direction;
    vec3 diffuse;
};
struct POINT
{   int on;
    int frag;
    vec3 position;
    vec3 diffuse;
    vec3 specular;
};

uniform AMBIENT lightAmbient;
uniform DIRECTIONAL lightDir;
uniform POINT lightPoint1, lightPoint2, lightPoint3;

layout (location = 0) in vec3 aVertex;
layout (location = 2) in vec3 aNormal;
layout (location = 3) in vec2 aTexCoord;

// Output (sent to Fragment Shader)
out vec4 color;
out vec4 position;
out vec4 normal;
out vec2 texCoord;
out float gravelFactor;     // gravelFactor is 1 within the gravel circle and 0 outside the circle

vec4 compAmbient(vec3 material, AMBIENT light)
{
    return vec4(material * light.color, 1);
}

vec4 compDirectional(vec3 material, DIRECTIONAL light)
{
    vec3 L = normalize(mat3(matrixView) * light.direction).xyz;
    float NdotL = dot(normal.xyz, L);
    if (NdotL > 0)
        return vec4(light.diffuse * material * NdotL, 1);
    else
        return vec4(0, 0, 0, 1);
}

vec4 compPoint(vec3 materialDiffuse, vec3 materialSpecular, float materialShininess, POINT light)
{
    vec4 result = vec4(0, 0, 0, 1);

    // diffuse
    vec3 L = normalize(matrixView * vec4(light.position, 1) - position).xyz;
    float NdotL = dot(L, normal.xyz);
    if (NdotL > 0)
        result += vec4(light.diffuse * materialDiffuse, 1) * NdotL;

    // specular
    vec3 V = normalize(-position.xyz);
    vec3 R = reflect(-L, normal.xyz);
    float RdotV = dot(R, V);
    if (NdotL > 0 && RdotV > 0)
        result += vec4(light.specular * materialSpecular * pow(RdotV, materialShininess), 1);

    //attentuation
    float dist = length(matrixView * vec4(light.position, 1) - position); 
    float att = 1 / (att_quadratic * dist * dist); 

    return result * att;
}

void main(void) 
{
    // calculate position & normal
    position = matrixModelView * vec4(aVertex, 1.0);
    gl_Position = matrixProjection * position;
    normal = vec4(normalize(mat3(matrixModelView) * aNormal), 1);

    // calculate texture coordinate
    texCoord = aTexCoord;

    // calculate the colour
    color = vec4(0, 0, 0, 0);

    // ambient light
    color += compAmbient(materialAmbient, lightAmbient);

    // directional lights
    color += compDirectional(materialDiffuse, lightDir);

    // point lights
    if (lightPoint1.on == 1 && lightPoint1.frag == 0)
        color += compPoint(materialDiffuse, materialSpecular, materialShininess, lightPoint1);
    if (lightPoint2.on == 1 && lightPoint2.frag == 0)
        color += compPoint(materialDiffuse, materialSpecular, materialShininess, lightPoint2);
    if (lightPoint3.on == 1 && lightPoint3.frag == 0)
        color += compPoint(materialDiffuse, materialSpecular, materialShininess, lightPoint3);

    // calculation of the gravelFactor:
    // 0 outside the 8-unit radius from the center
    // 1 within 7 units from the centre
    // between 0 and 1 at the border zone
    gravelFactor = clamp(-length(aVertex.xz) + 8, 0, 1);
}

Fragment Shader:片段着色器:

// input variables
in vec4 color;
in vec4 position;
in vec4 normal;
in vec2 texCoord;
in float gravelFactor;

// output variable
out vec4 outColor;

// uniforms - material parameters
uniform vec3 materialAmbient;
uniform vec3 materialDiffuse;
uniform vec3 materialSpecular;
uniform float materialShininess;

uniform float att_quadratic = 0.1;

// This uniform variable may be used to take different actions for the terrain and not-terrain
uniform int terrain;

// view matrix (needed for lighting)
uniform mat4 matrixView;

struct POINT
{   int on;
    int frag;
    vec3 position;
    vec3 diffuse;
    vec3 specular;
};

uniform POINT lightPoint1, lightPoint2, lightPoint3;

vec4 compPoint(vec3 materialDiffuse, vec3 materialSpecular, float materialShininess, POINT light)
{
    vec4 result = vec4(0, 0, 0, 1);

    // diffuse
    vec3 L = normalize(matrixView * vec4(light.position, 1) - position).xyz;
    float NdotL = dot(L, normal.xyz);
    if (NdotL > 0)
        result += vec4(light.diffuse * materialDiffuse, 1) * NdotL;

    // specular
    vec3 V = normalize(-position.xyz);
    vec3 R = reflect(-L, normal.xyz);
    float RdotV = dot(R, V);
    if (NdotL > 0 && RdotV > 0)
        result += vec4(light.specular * materialSpecular * pow(RdotV, materialShininess), 1);

    //attentuation
    float dist = length(matrixView * vec4(light.position, 1) - position); 
    float att = 1 / (att_quadratic * dist * dist); 

    return result * att;
}

// Texture Samplers
uniform sampler2D textureGrass;
uniform sampler2D textureGravel;
uniform sampler2D texture;
uniform sampler2D bumpmap;
uniform sampler2D textureNormal;

vec4 bump_normal = texture(textureNormal, texCoord.st) * 2 - 1; 

void main(void) 
{
    outColor = color;

    if (lightPoint1.on == 1 && lightPoint1.frag == 1)
        outColor += compPoint(materialDiffuse, materialSpecular, materialShininess, lightPoint1);
    if (lightPoint2.on == 1 && lightPoint2.frag == 1)
        outColor += compPoint(materialDiffuse, materialSpecular, materialShininess, lightPoint2);
    if (lightPoint3.on == 1 && lightPoint3.frag == 1)
        outColor += compPoint(materialDiffuse, materialSpecular, materialShininess, lightPoint3);

    if (terrain == 1)
    {
        // Rendering Terrain
        // Terrain is a mix of the Grass and Gravel texture
        outColor *= mix(texture(textureGrass, texCoord.st), texture(textureGravel, texCoord.st), gravelFactor);
    }
    else
    {
        outColor *= texture(texture, texCoord.st) + bump_normal;
    }
}

And the declaration of my point lights:以及我的点灯声明:

//setup point light1
glUniform1i(glGetUniformLocation(idProg, "lightPoint1.on"), 1);
glUniform1i(glGetUniformLocation(idProg, "lightPoint1.frag"), 1);
glUniform3f(glGetUniformLocation(idProg, "lightPoint1.position"), 0, 3.1, 0.0);
glUniform3f(glGetUniformLocation(idProg, "lightPoint1.diffuse"), 1.0, 0.0, 0.0);
glUniform3f(glGetUniformLocation(idProg, "lightPoint1.specular"),  1.0, 0.0, 0.0);

What would be the best way to approach this problem?解决这个问题的最佳方法是什么? Should the position of the light be changed within the main code or within the shaders?光的位置应该在主代码中还是在着色器中改变? How could I do this?我怎么能这样做?

I would definitely change the light position in your main code and then pass it to your shaders.我肯定会在您的主代码中更改灯光位置,然后将其传递给您的着色器。

So, for instance, your code to change the position would look something like this:因此,例如,您更改位置的代码如下所示:

//called whenever you redraw your scene
void render() 
{
    //or however you want to position the light relative to your camera
    glUniform3f
        (
        glGetUniformLocation(idProg, "lightPoint1.position"), 
        get_camera_pos_x(), 
        get_camera_pos_y(), 
        get_camera_pos_z()
        );
    glUniform3f
        (            
        glGetUniformLocation(idProg, "lightPoint1.direction"), 
        get_camera_dir_x(), 
        get_camera_dir_y(), 
        get_camera_dir_z()
        )
    //...rest of your drawing code...
}

The main advantage of doing it this way is you are eliminating redundant calculations.这样做的主要优点是您可以消除冗余计算。 If you had your vertex shader updating the light position in response to your camera position, it would work but you would be repeating that calculation many times each frame.如果您的顶点着色器根据您的相机位置更新灯光位置,它会起作用,但您将在每一帧中多次重复该计算。 Remember, a vertex shader is executed on every vertex that you draw.请记住,顶点着色器会在您绘制的每个顶点上执行。 There's no need to recalculate the light's position on each vertex if it's going to be the same each time.如果每次都相同,则无需重新计算每个顶点上的灯光位置。

Update per OP in comments: OP stated that he wanted to be able to change the direction of the light using the camera, like a flashlight.在评论中更新每个 OP:OP 表示他希望能够使用相机改变光线的方向,就像手电筒一样。 To do this, you will need to add an additional uniform to your light struct in the shader.为此,您需要在着色器中为您的灯光结构添加额外的制服。 I've called it "direction" (a normalized vec3) above.我在上面称之为“方向”(标准化的 vec3)。 You can then calculate the camera's direction in your main code and pass it to the shader like normal.然后,您可以在主代码中计算相机的方向,并像平常一样将其传递给着色器。 What you do with it in the shader is up to you, but this tutorial might help您在着色器中使用它做什么取决于您,但本教程可能会有所帮助

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM