如何将光照添加到实例化的着色器中

How to add lighting to shader of instancing

如何向与 InstancedBufferGeometry 一起使用的着色器添加照明(环境光 + 方向光)?

例如,我想为此添加照明: https://threejs.org/examples/?q=inst#webgl_buffergeometry_instancing_dynamic

这是我的顶点着色器:

precision highp float;
uniform mat4 modelViewMatrix;
uniform mat4 projectionMatrix;
uniform mat3 normalMatrix;
attribute vec3 position;
attribute vec3 offset;
attribute vec3 normal;
attribute vec2 uv;
attribute vec4 orientation;
varying vec2 vUv;

// lighting
struct DirectionalLight {
    vec3 direction;
    vec3 color;
    int shadow;
    float shadowBias;
    float shadowRadius;
    vec2 shadowMapSize;
    };
uniform DirectionalLight directionalLights[ NUM_DIR_LIGHTS ];
uniform vec3 ambientLightColor;
varying vec3 vLightFactor;
//

void main() {
    vec3 vPosition = position;
    vec3 vcV = cross(orientation.xyz, vPosition);
    vPosition = vcV * (2.0 * orientation.w) + (cross(orientation.xyz, vcV) * 2.0 + vPosition);
    vUv = uv;
    gl_Position = projectionMatrix * modelViewMatrix * vec4( offset + vPosition, 1.0 );

    // lighting
    vec4 ecPosition = modelViewMatrix*vec4(offset + vPosition,1.0);
    vec3 ecNormal= -(normalize(normalMatrix*normal));
    vec3 fromLight = normalize(directionalLights[0].direction);
    vec3 toLight = -fromLight;
    vec3 reflectLight = reflect(toLight,ecNormal);
    vec3 viewDir = normalize(-ecPosition.xyz);
    float ndots = dot(ecNormal, toLight);
    float vdotr = max(0.0,dot(viewDir,reflectLight));
    vec3 ambi = ambientLightColor;
    vec3 diff = directionalLights[0].color * ndots;
    vLightFactor = ambi + diff;     
    //

}

这是我的片段着色器:

precision highp float;
uniform sampler2D map;
varying vec2 vUv;

// lighting
varying vec3 vLightFactor;
//

void main() {
    gl_FragColor = texture2D(map, vUv) * vec4(vLightFactor,1.0);
}

这是我的 material:

var uniforms = Object.assign( 
    THREE.UniformsLib['lights'], 
    {
    map: { value: texture }
    }
);  

var material = new THREE.RawShaderMaterial({
    lights: true,
    uniforms: uniforms, 
    vertexShader: document.getElementById( 'vertexShader' ).textContent,
    fragmentShader: document.getElementById( 'fragmentShader' ).textContent,
});

谢谢

在渲染中,场景的每个网格通常由模型矩阵、视图矩阵和投影矩阵进行变换。

模型矩阵定义场景中网格的位置、方向和相对大小。模型矩阵将顶点位置从网格变换到世界space。

视图矩阵描述了观察场景的方向和位置。视图矩阵从世界 space 转换为视图(眼睛)space。

注意,模型视图矩阵modelViewMatrix是视图矩阵和模型矩阵的组合。但在您的情况下,模型矩阵可能是 单位矩阵 ,而 modelViewMatrix 可能等于视图矩阵。我假设,因为您不是通过模型矩阵进行模型转换,而是通过向量 orientationoffset.

灯光可以在视图 space 或世界 space 中计算。
如果灯光是在视图 space 中计算的,则灯光位置和灯光方向必须从世界 space 转换到视图 space。通常这是在 CPU(每帧之前)上完成的,并且灯光参数制服是使用视图 space 坐标设置的。由于视图位置在视图 space 中为 (0, 0, 0),因此视图向量是归一化和逆顶点位置(在视图 space 中)。
如果光照计算是在world space中完成的,那么view vector必须通过view position(眼睛位置)和顶点位置(当然在world space中)的差来计算。


您可以在视图 space 中进行光照计算,因为光照方向和位置是在视图 space 中设置的(参见 three.js - Light)。您必须先将法向量转换为世界 space,然后您必须从世界 space 转换为视图 space。这必须与您对顶点位置所做的类似。将法向量添加到顶点位置。将此位置转换为世界 space。世界space中的法向量是计算出的位置与世界space中顶点位置的差值space.

vec3 wNPosition = position + normal;
vec3 wNV        = cross(orientation.xyz, wNPosition);
wNPosition      = wNV * 2.0 * orientation.w + cross(orientation.xyz, wNV) * 2.0 + wNPosition;
vec3 wNormal    = normalize( wNPosition - vPosition );


在此假设下,您的着色器代码可能如下所示:

vec3 wPosition       = position;
vec3 wV              = cross(orientation.xyz, wPosition);
wPosition            = offset + wV * 2.0 * orientation.w + cross(orientation.xyz, wV) * 2.0 + wPosition;
vec4 ecPosition      = modelViewMatrix * vec4(wPosition, 1.0);
vUv                  = uv;
gl_Position          = projectionMatrix * ecPosition;

// transform normal vector to world space
vec3 wNPosition      = position + normal;
vec3 wNV             = cross(orientation.xyz, wNPosition);
wNPosition           = offset + wNV * 2.0 * orientation.w + cross(orientation.xyz, wNV) * 2.0 + wNPosition;
vec3 ecNormal        = normalize(mat3(modelViewMatrix) * (wNPosition - wPosition));

// ambient light
vLightFactor         = ambientLightColor;

// diffuse light
vec3  ecToLight      = normalize(directionalLights[0].direction);
float NdotL          = max(0.0, dot(ecNormal, ecToLight));
vLightFactor        += NdotL * directionalLights[0].color; 


如果你想添加镜面光,你必须这样做:

// specular light
vec3  ecReflectLight = reflect( ecFromLight, ecNormal );
vec3  ecViewDir      = normalize(-ecPosition.xyz);
float VdotR          = max(0.0, dot(ecViewDir, ecReflectLight));
float kSpecular      = 4.0 * pow( VdotR, 0.3 * shininess ); // <--- set up shininess parameter
vLightFactor        += kSpecular * directionalLights[0].color;


答案扩展:每个片段光照

Gouraud shading calculates the light in the the vertex shader, 在片段着色器中计算光照。
(进一步查看 GLSL fixed function fragment program replacement

顶点着色器:

precision highp float;

uniform mat4 modelViewMatrix;
uniform mat4 projectionMatrix;
uniform mat3 normalMatrix;

attribute vec3 position;
attribute vec3 offset;
attribute vec3 normal;
attribute vec2 uv;
attribute vec4 orientation;

varying vec2 vUv;
varying vec3 ecPosition;
varying vec3 ecNormal;

void main()
{
    vec3 wPosition   = position;
    vec3 wV          = cross(orientation.xyz, wPosition);
    pos              = offset + wV * 2.0 * orientation.w + cross(orientation.xyz, wV) * 2.0 + wPosition;
    vec4 vPos        = modelViewMatrix * vec4(wPosition, 1.0);
    ecPosition       = vPos.xyz;
    vUv              = uv;
    gl_Position      = projectionMatrix * vPos;

    // transform normal vector to world space
    vec3 wNPosition  = position + normal;
    vec3 wNV         = cross(orientation.xyz, wNPosition);
    wNPosition       = offset + wNV * 2.0 * orientation.w + cross(orientation.xyz, wNV) * 2.0 + wNPosition;
    ecNormal         = normalize(mat3(modelViewMatrix) * (wNPosition - wPosition));
}

片段着色器:

precision highp float;

varying vec2 vUv;
varying vec3 ecPosition;
varying vec3 ecNormal;

uniform sampler2D map;
uniform mat4 modelViewMatrix;

struct DirectionalLight {
    vec3 direction;
    vec3 color;
    int shadow;
    float shadowBias;
    float shadowRadius;
    vec2 shadowMapSize;
};
uniform DirectionalLight directionalLights[ NUM_DIR_LIGHTS ];
uniform vec3 ambientLightColor;

void main()
{
    // ambient light
    float lightFactor = ambientLightColor;

    // diffuse light
    vec3  ecToLight      = normalize(directionalLights[0].direction);
    float NdotL          = max(0.0, dot(ecNormal, ecToLight));
    lightFactor         += NdotL * directionalLights[0].color; 

    // specular light
    vec3  ecReflectLight = reflect( ecFromLight, ecNormal );
    vec3  ecViewDir      = normalize(-ecPosition.xyz);
    float VdotR          = max(0.0, dot(ecViewDir, ecReflectLight));
    float kSpecular      = 4.0 * pow( VdotR, 0.3 * shininess ); // <--- set up shininess parameter
    lightFactor         += kSpecular * directionalLights[0].color;

    gl_FragColor = texture2D(map, vUv) * vec4(vec3(lightFactor), 1.0);
}


另见

  • How does this faking the light work on aerotwist?
  • GLSL fixed function fragment program replacement

我的意思是片段着色器应该是这样的:

precision highp float;

varying vec2 vUv;
varying vec3 ecPosition;
varying vec3 ecNormal;

uniform sampler2D map;
uniform mat4 modelViewMatrix;

struct DirectionalLight {
    vec3 direction;
    vec3 color;
    int shadow;
    float shadowBias;
    float shadowRadius;
    vec2 shadowMapSize;
};
uniform DirectionalLight directionalLights[ NUM_DIR_LIGHTS ];
uniform vec3 ambientLightColor;

void main()
{
    // ambient light
    vec3 lightFactor = ambientLightColor;

    // diffuse light
    vec3  ecFromLight    = normalize(directionalLights[0].direction);
    //vec3  ecToLight      = -ecFromLight;
    float NdotL          = max(0.0, dot(ecNormal, ecFromLight));
    lightFactor         += NdotL * directionalLights[0].color; 

    // specular light
    /*
    float shininess = 10.01;
    vec3  ecReflectLight = reflect( ecFromLight, ecNormal );
    vec3  ecViewDir      = normalize(-ecPosition.xyz);
    float VdotR          = max(0.0, dot(ecViewDir, ecReflectLight));
    float kSpecular      = 4.0 * pow( VdotR, 0.3 * shininess ); // <--- set up shininess parameter
    lightFactor         += kSpecular * directionalLights[0].color;
    */

    gl_FragColor = texture2D(map, vUv) * vec4(lightFactor, 1.0);
}