如何为实例化着色器添加光照

时间:2017-09-22 21:11:43

标签: three.js light

如何向与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);
}

这是我的材料:

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,
});

由于

2 个答案:

答案 0 :(得分:2)

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

模型矩阵定义场景中网格的位置,方向和相对大小。模型矩阵将顶点位置从网格转换为世界空间。

视图矩阵描述了查看场景的方向和位置。视图矩阵从世界空间转换到视图(眼睛)空间。

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

光可以在视野空间或世界空间中计算 如果在视图空间中计算光,则光位置和光方向必须从世界空间转换到视图空间。通常这是在CPU上完成的(在每帧之前),并且使用视图空间坐标设置光参数制服。由于视图空间中的视图位置为(0,0,0),因此视图矢量是标准化和反向顶点位置(在视图空间中)。
如果光计算在世界空间中完成,则视图矢量必须通过视图位置(眼睛位置)和顶点位置(当然在世界空间中)的差异来计算。


您可以在视图空间中进行灯光计算,因为灯光方向和位置是在视图空间中设置的(请参阅three.js - Light)。您必须首先将法线向量转换为世界空间,然后必须从世界空间转换为视图空间。这必须与您对顶点位置的操作类似。将法线向量添加到顶点位置。将这个位置转变为世界空间。世界空间中的法线向量是计算出的位置与世界空间中顶点位置的差异。

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计算顶点着色器中的灯光时,Phong shading会计算片段着色器中的灯光。
(进一步参见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);
}


另见

答案 1 :(得分:0)

我的意思是片段着色器应该如下所示:

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);
}