ThreeJS FBO 粒子光照和阴影
ThreeJS FBO Particle Lighting and Shadows
更新:我决定根据 ScieCode 建议的 link、http://blog.edankwan.com/post/three-js-advanced-tips-shadow 实施一个简单的照明解决方案。这是 linked 页面的第一部分。结果可以在这个post.
底部的图片中看到
linked 页面确实包含有关如何在 ThreeJS 中使用自定义着色器 material 投射阴影的信息,但我觉得它更适合网格和基本粒子系统。我正在使用 FBO 粒子系统。
我有一个使用 FBO 的粒子系统。它是使用 ThreeJS 中的着色器 materials 构建的。粒子变成高耸的积云。如何将光照和阴影应用到这些粒子,就好像它们是一个实体网格一样?
下面是将粒子渲染到屏幕的最后一组着色器:
老实说,我什至不完全理解顶点着色器是如何输出位置的。我找到了这个示例代码并适合我正在做的事情。我了解其中的大部分内容,但顶点着色器的尾端对我来说很模糊。
顶点:
precision highp float;
attribute vec2 id;
attribute vec3 p_color;
attribute float p_size;
uniform vec2 dimensions;
uniform float size;
uniform sampler2D infoTexture;
uniform sampler2D originalTexture;
uniform sampler2D positionTexture;
uniform sampler2D previousPositionTexture;
uniform sampler2D velocityTexture;
varying vec2 vUv;
varying float life;
varying vec2 vSpeed;
varying float vSize;
float modI(float a,float b) {
float m=a-floor((a+0.5)/b)*b;
return floor(m+0.5);
}
const float PI = 3.14159265359;
void main() {
float size_modifier = 1. * p_size;
float ptr = id.x;
float u = modI( ptr, dimensions.x ) / dimensions.x;
float v = ( ptr / dimensions.x ) / dimensions.y;
vec2 puv = vec2( u, v );
vec4 velocity = texture2D( velocityTexture, puv );
// vSpeed = .1 * ( projectionMatrix * modelViewMatrix * vec4( velocity.xyz, 1. ) ).xy;
vec4 i = texture2D( infoTexture, puv );
vec4 prev_pos = texture2D( previousPositionTexture, puv );
vec4 origin_pos = texture2D( originalTexture, puv );
vec4 c = texture2D( positionTexture, puv );
vec3 p = c.xyz;
vUv = uv;
float life_time = 1000.;
life = 1. - ( c.a / life_time );
// if( c.a == 0. ) life = 0.;
if( velocity.a != 1. ){
size_modifier = .0;
}
vec4 modified = modelViewMatrix * vec4( p, 1. );
// float a = -atan( vSpeed.y, vSpeed.x ) - .5 * PI;
// float l = clamp( length( vSpeed ), .5, 4. );
// mat2 rot = mat2( cos( a ), -sin( a ), sin( a ), cos( a ) );
// modified.xyz += size * i.x * 10. * vec3( rot * position.xy, 0. );
// modified.xyz += size * size_modifier * i.x * 10. * vec3( rot * position.xy, 0. );
modified.xyz += size * size_modifier * i.x * 10. * vec3( position.xy, 0. );
gl_Position = projectionMatrix * modified;
}
片段:
precision highp float;
varying float vSize;
varying vec2 vUv;
varying float life;
varying vec2 vSpeed;
varying vec3 p_color_f;
uniform float useTriangles;
const vec2 barycenter = vec2( .5, .6666 );
void main() {
// render circles
float d = smoothstep( .5, .55, 1. - 2. * length( vUv - barycenter ) );
if( d <= 0. ) discard;
vec3 frag_c = vec3( p_color_f );
gl_FragColor = vec4( frag_c, 1.);
}
我如何将着色器块添加到自定义着色器:
particleMaterial = new THREE.ShaderMaterial( {
uniforms: THREE.UniformsUtils.merge([
THREE.UniformsLib.shadowmap,
THREE.UniformsLib.lights,
THREE.UniformsLib.ambient,
{
size: { type: 'f', value: 1 },
useTriangles: { type: 'f', value: 0 },
originalTexture: { type: 't', value: texture },
infoTexture: { type: 't', value: texture2 },
positionTexture: { type: 't', value: positionSim.fbos[ 0 ] },
previousPositionTexture: { type: 't', value: positionSim.fbos[ 1 ] },
velocityTexture: { type: 't', value: velocitySim.fbos[ 1 ] },
dimensions: { type: 'v2', value: dimensions },
cameraPosition: { type: 'v3', value: new THREE.Vector3() },
prevModelViewMatrix: { type: 'm4', value: new THREE.Matrix4() },
prevProjectionMatrix: { type: 'm4', value: new THREE.Matrix4() }
} ]),
vertexShader: document.getElementById( 'particle-vs' ).textContent,
fragmentShader: document.getElementById( 'particle-fs' ).textContent,
transparent: false,
side: THREE.DoubleSide,
depthTest: true,
depthWrite: true,
lights: true
// blending: THREE.NoBlending
} );
基本光照结果:
即便如此,也为风暴增加了很多深度。
不太清楚您希望阴影或光照如何表现。如果您能说得更清楚,我很乐意编辑此回复以最好地回答您的问题。
话虽如此,有两种资源可能适用于您的情况:
- Edan Kwan 关于自定义着色器/粒子阴影的 blog post。它有点过时,但其中大部分仍然有效,应该能让您对您要解决的问题有一个正确的理解。
- 自从 Edan 的 post 制作以来,一些 GLSL ShaderChunks 发生了变化,我建议你看看这个 discourse post 我做的,当我尝试听起来类似于你在问什么。
为了模拟正确的光照,您需要导入 light_pars
ShaderChunks 及其所有依赖项。这样,您就可以访问每个粒子顶点位置上的光强度。您可以按照自己认为合适的方式使用该信息。
更新:我决定根据 ScieCode 建议的 link、http://blog.edankwan.com/post/three-js-advanced-tips-shadow 实施一个简单的照明解决方案。这是 linked 页面的第一部分。结果可以在这个post.
底部的图片中看到linked 页面确实包含有关如何在 ThreeJS 中使用自定义着色器 material 投射阴影的信息,但我觉得它更适合网格和基本粒子系统。我正在使用 FBO 粒子系统。
我有一个使用 FBO 的粒子系统。它是使用 ThreeJS 中的着色器 materials 构建的。粒子变成高耸的积云。如何将光照和阴影应用到这些粒子,就好像它们是一个实体网格一样?
下面是将粒子渲染到屏幕的最后一组着色器:
老实说,我什至不完全理解顶点着色器是如何输出位置的。我找到了这个示例代码并适合我正在做的事情。我了解其中的大部分内容,但顶点着色器的尾端对我来说很模糊。
顶点:
precision highp float;
attribute vec2 id;
attribute vec3 p_color;
attribute float p_size;
uniform vec2 dimensions;
uniform float size;
uniform sampler2D infoTexture;
uniform sampler2D originalTexture;
uniform sampler2D positionTexture;
uniform sampler2D previousPositionTexture;
uniform sampler2D velocityTexture;
varying vec2 vUv;
varying float life;
varying vec2 vSpeed;
varying float vSize;
float modI(float a,float b) {
float m=a-floor((a+0.5)/b)*b;
return floor(m+0.5);
}
const float PI = 3.14159265359;
void main() {
float size_modifier = 1. * p_size;
float ptr = id.x;
float u = modI( ptr, dimensions.x ) / dimensions.x;
float v = ( ptr / dimensions.x ) / dimensions.y;
vec2 puv = vec2( u, v );
vec4 velocity = texture2D( velocityTexture, puv );
// vSpeed = .1 * ( projectionMatrix * modelViewMatrix * vec4( velocity.xyz, 1. ) ).xy;
vec4 i = texture2D( infoTexture, puv );
vec4 prev_pos = texture2D( previousPositionTexture, puv );
vec4 origin_pos = texture2D( originalTexture, puv );
vec4 c = texture2D( positionTexture, puv );
vec3 p = c.xyz;
vUv = uv;
float life_time = 1000.;
life = 1. - ( c.a / life_time );
// if( c.a == 0. ) life = 0.;
if( velocity.a != 1. ){
size_modifier = .0;
}
vec4 modified = modelViewMatrix * vec4( p, 1. );
// float a = -atan( vSpeed.y, vSpeed.x ) - .5 * PI;
// float l = clamp( length( vSpeed ), .5, 4. );
// mat2 rot = mat2( cos( a ), -sin( a ), sin( a ), cos( a ) );
// modified.xyz += size * i.x * 10. * vec3( rot * position.xy, 0. );
// modified.xyz += size * size_modifier * i.x * 10. * vec3( rot * position.xy, 0. );
modified.xyz += size * size_modifier * i.x * 10. * vec3( position.xy, 0. );
gl_Position = projectionMatrix * modified;
}
片段:
precision highp float;
varying float vSize;
varying vec2 vUv;
varying float life;
varying vec2 vSpeed;
varying vec3 p_color_f;
uniform float useTriangles;
const vec2 barycenter = vec2( .5, .6666 );
void main() {
// render circles
float d = smoothstep( .5, .55, 1. - 2. * length( vUv - barycenter ) );
if( d <= 0. ) discard;
vec3 frag_c = vec3( p_color_f );
gl_FragColor = vec4( frag_c, 1.);
}
我如何将着色器块添加到自定义着色器:
particleMaterial = new THREE.ShaderMaterial( {
uniforms: THREE.UniformsUtils.merge([
THREE.UniformsLib.shadowmap,
THREE.UniformsLib.lights,
THREE.UniformsLib.ambient,
{
size: { type: 'f', value: 1 },
useTriangles: { type: 'f', value: 0 },
originalTexture: { type: 't', value: texture },
infoTexture: { type: 't', value: texture2 },
positionTexture: { type: 't', value: positionSim.fbos[ 0 ] },
previousPositionTexture: { type: 't', value: positionSim.fbos[ 1 ] },
velocityTexture: { type: 't', value: velocitySim.fbos[ 1 ] },
dimensions: { type: 'v2', value: dimensions },
cameraPosition: { type: 'v3', value: new THREE.Vector3() },
prevModelViewMatrix: { type: 'm4', value: new THREE.Matrix4() },
prevProjectionMatrix: { type: 'm4', value: new THREE.Matrix4() }
} ]),
vertexShader: document.getElementById( 'particle-vs' ).textContent,
fragmentShader: document.getElementById( 'particle-fs' ).textContent,
transparent: false,
side: THREE.DoubleSide,
depthTest: true,
depthWrite: true,
lights: true
// blending: THREE.NoBlending
} );
基本光照结果:
即便如此,也为风暴增加了很多深度。
不太清楚您希望阴影或光照如何表现。如果您能说得更清楚,我很乐意编辑此回复以最好地回答您的问题。
话虽如此,有两种资源可能适用于您的情况:
- Edan Kwan 关于自定义着色器/粒子阴影的 blog post。它有点过时,但其中大部分仍然有效,应该能让您对您要解决的问题有一个正确的理解。
- 自从 Edan 的 post 制作以来,一些 GLSL ShaderChunks 发生了变化,我建议你看看这个 discourse post 我做的,当我尝试听起来类似于你在问什么。
为了模拟正确的光照,您需要导入 light_pars
ShaderChunks 及其所有依赖项。这样,您就可以访问每个粒子顶点位置上的光强度。您可以按照自己认为合适的方式使用该信息。