了解 OpenGL 中的光与 Material 属性

Understanding Light vs Material Properties in OpenGL

我正在为延迟渲染重构我的引擎,并尝试在 OpenGL 2.1 / GLSL 120 中实现适当的照明。到目前为止,我相信我已经实现了基于环境光颜色的环境组件作为定向光、点光和聚光灯的漫反射组件。然而,当涉及到这些光类型的镜面反射分量时,我变得有点难过。这是延迟着色器的第二个(光照)通道的当前代码:

light_pass.vs

#version 120

attribute vec2 a_position;
uniform mat4 u_projection;
uniform mat4 u_view;

void main()
{
    gl_Position = u_projection * u_view * vec4(a_position, 0.0, 1.0);
}

light_pass.fs

#version 120

const int AMBIENT = 0;
const int DIRECTIONAL = 1;
const int POINT = 2;
const int SPOT = 3;

struct Light
{
    int type;
    vec3 color;
    vec3 direction;
    vec3 position;
    float radius;
    float inner_angle;
    float outer_angle;
};

uniform vec2 u_screen_size;
uniform Light u_light;
uniform sampler2D u_position;
uniform sampler2D u_normal;
uniform sampler2D u_diffuse;

vec2 calc_texcoord()
{
    return gl_FragCoord.xy / u_screen_size;
}

vec4 calc_ambient_light()
{
    return vec4(u_light.color, 1.0);
}

vec4 calc_directional_light(vec3 position, vec3 normal)
{
    vec3 light_dir = normalize(-u_light.direction);
    float contribution = max(dot(normal, light_dir), 0.0);
    return vec4(contribution * u_light.color, 1.0);
}

vec4 calc_point_light(vec3 position, vec3 normal)
{
    float dist = length(u_light.position - position);
    float att = clamp(1.0 - (dist*dist)/(u_light.radius*u_light.radius), 0.0, 1.0); att *= att;
    vec3 light_dir = normalize(u_light.position - position);
    float contribution = max(0.0, dot(normal, light_dir));
    return vec4(contribution * u_light.color, 1.0) * att;
}

vec4 calc_spot_light(vec3 position, vec3 normal)
{
    float dist = length(u_light.position - position);
    float att = clamp(1.0 - (dist*dist)/(u_light.radius*u_light.radius), 0.0, 1.0); att *= att;
    vec3 light_dir = normalize(u_light.position - position);
    float contribution = max(0.0, dot(normal, light_dir));
    vec3 spot_dir = normalize(u_light.direction);
    float angle = acos(dot(light_dir, spot_dir));
    float angle_factor = 1 - smoothstep(u_light.inner_angle, u_light.outer_angle, angle);
    return vec4(contribution * u_light.color, 1.0) * angle_factor * att;
}

void main()
{
    vec2 texcoord = calc_texcoord();
    vec3 position = texture2D(u_position, texcoord).xyz;
    vec3 normal = texture2D(u_normal, texcoord).xyz;
    vec3 diffuse = texture2D(u_diffuse, texcoord).xyz;
    //normal = normalize(normal);
    vec4 result = vec4(0, 0, 0, 0);

    int type = u_light.type;
    if(type == AMBIENT)
        result = calc_ambient_light();
    else if(type == DIRECTIONAL)
        result = calc_directional_light(position, normal);
    else if(type == POINT)
        result = calc_point_light(position, normal);
    else if(type == SPOT)
        result = calc_spot_light(position, normal);

    gl_FragColor = vec4(diffuse, 1.0) * result;//vec4(texcoord, 0, 1);
}

下面是我目前在我的灯光中使用的属性 class(忽略阴影属性)来定义每种类型的灯光:

light.py

from enum import Enum
from pyorama.core.entity import Entity

class LightTypes(Enum):
    AMBIENT = 0
    DIRECTIONAL = 1
    POINT = 2
    SPOT = 3

class AmbientLight(Entity):
    def __init__(self, color):
        self.color = color
        self.cast_shadow = False
        super(AmbientLight, self).__init__()

class DirectionalLight(Entity):
    def __init__(self, color, direction):
        self.color = color
        self.direction = direction
        self.cast_shadow = False
        super(DirectionalLight, self).__init__()

class PointLight(Entity):
    def __init__(self, color, position, radius):
        self.color = color
        self.position = position
        self.radius = radius
        self.cast_shadow = False
        super(PointLight, self).__init__()

class SpotLight(Entity):
    def __init__(self, color, position, radius, direction, inner_angle, outer_angle):
        self.color = color
        self.position = position
        self.radius = radius
        self.direction = direction
        self.inner_angle = inner_angle
        self.outer_angle = outer_angle
        self.cast_shadow = False
        super(SpotLight, self).__init__()

目前,我还没有定义 material class,我将与我的网格关联的纹理视为模型漫反射组件的来源。 关于照明我最大的问题是了解哪些属性属于光,哪些属于 material。

如果我查看 3d 模型的典型 .mtl (material) 文件,我发现在最基本的级别上,有 Ka、Kd、Ks 和 Ns 属性,对应于环境 rgb 值、漫反射 rgb 值、镜面反射 rgb 值和神秘的镜面反射指数浮点数。灯也有这些相同的组件吗?光和 material 组件是如何组合在一起的?许多使用 OpenGL 2.1 的在线教程使用固定函数术语和 glLightandglMaterial*` 调用,这进一步增加了我的困惑。

此外,如果我有一个带纹理的模型,这些颜色不是由纹理颜色而不是单独的 material 属性定义的吗?目前,我没有 material class 并且只是假设漫反射颜色是在我的片段着色器中采样的插值点处的纹理颜色。 Material class 应该有哪些属性列表?我当前的 Lighting class 中是否有任何 missing/incorrect 属性? 我对任何代码都不是特别感兴趣,只是解释我需要组合哪些灯光和 material 属性在具有环境 + 漫反射 + 镜面反射组件的基本 Blinn-Phong 照明模型中获得最终颜色输出。任何帮助将不胜感激。

我假设我们不是在谈论 BRDF 光照,因为 PBR 着色模型可能包含许多 material properties.In 你的情况我们处理标准的 ADS 着色模型,其中:

A- 环境色

D-漫反射色

S- 高光颜色。

尽管对漫反射和镜面反射项使用相同的颜色,除非你追求一些非常奇特的灯光效果。

光照模型也称为Blinn-Phong,归结为以下伪方程:

阴影像素 = 环境项 + 漫反射项 + 镜面反射项

现在,当然,你不只是将这些制服传递的值放入过于简化的 equation.Ambient 颜色通常是原样,而最终的漫反射和镜面反射项 are calculated 基于关于光、视图方向之间的矢量产品评估。 但是ADS是任何灯光的基本属性type.In 除了这些,一些灯光类型还有额外的properties.For例子,Spot light有锥角和Radius,甚至衰减羽化属性来控制灯光cone.It 也很常见 光强度 属性 作为标量,乘以漫反射,以及镜面反射项以加强光强度。

关于material属性,你得到的是标准属性:

MA - 环境反射率

MD - 漫反射率

MS - 镜面反射率

光泽度 -(标量),镜面反射光泽度因子

这些你将作为 material 的一部分传递到你的程序中 uniforms.The 前 3 个在最终的着色公式中组合,其中它们与灯光道具中的对应项相乘 (见顶部)。Shininess factor 用于修改 surface.It 上的镜面效果量,请务必注意,如果您使用 MA、MD、MS 或 not.Basically,则取决于您有一个 material 的漫反射颜色 属性,它是 enough.Shininess 虽然这是一个非常重要的道具,因为它控制表面上的镜面反射扩散量 当然,您可以自由地为灯光和 materials 制作和使用额外的道具。Here is 一本关于 ADS 着色的好书。