vlambda博客
学习文章列表

Threejs多重纹理与过程纹理实现

Author--------------- Yen

      多数时候同一图元是采用单一纹理,但是为了增强显示效果,会存在同一图元多个纹理图的情况,效果比如只有灯光照射的时候才会显示其他图案,其他没有被照射到的地方显示正常灰色,边缘区域是平滑过渡的。这样就为图案的显隐更加增添一丝神秘气息,效果如下:



1)对同一个图元采用多幅纹理图,这种技术称为多重纹理

2)在多重纹理变化的边界根据某种规则进行平滑过渡效果,这种技术称为过程纹理

这种平滑过渡在很多情况下都会用到,如有的时候白天黑夜不同图片的过渡,还有丘陵地形中根据不同海拔不同的纹理进行过渡。

具体实现核心代码如下

自定义材质ShaderMaterial

 var textureLoader = new THREE.TextureLoader(); // 需要增加的纹理贴图 var map = textureLoader.load( "textures/sprites/timg.jpg" ); // 自定义材质 var material = new THREE.ShaderMaterial({ uniforms: THREE.UniformsUtils.merge([ THREE.UniformsLib.common, THREE.UniformsLib.specularmap, THREE.UniformsLib.envmap, THREE.UniformsLib.aomap, THREE.UniformsLib.lightmap, THREE.UniformsLib.emissivemap, THREE.UniformsLib.bumpmap, THREE.UniformsLib.normalmap, THREE.UniformsLib.displacementmap, THREE.UniformsLib.gradientmap, THREE.UniformsLib.fog, THREE.UniformsLib.lights, { emissive: { value: new THREE.Color( 0x000000 ) }, specular: { value: new THREE.Color( 0x111111 ) }, shininess: { value: 30 }, map: { value: undefined } } ]), vertexShader: THREE.ShaderChunk.meshphong_vert, fragmentShader: THREE.ShaderChunk.meshphong_frag }); material.lights = true; material.uniforms.map.value = map; var geometry = new THREE.PlaneBufferGeometry( 300, 200 ); var mesh = new THREE.Mesh( geometry, material );

其中比较重要的是修改THREE.ShaderChunk.meshphong_frag的源码(..src\renderers\shaders\ShaderLib\meshphong_frag.glsl.js),修改的核心代码:

// yen 增加的变量定义varying vec2 vUv; // 记得对应的顶点着色器中也要增加UVfloat vIntensity;uniform sampler2D map;
......// yen vec4 mapColor = texture2D(map, vec2(vUv.x, vUv.y)); if (vIntensity > 0.21) { // 光照强度大于0.21时候显示添加的纹理 gl_FragColor = vec4( vec3(mapColor.r, mapColor.g, mapColor.b), diffuseColor.a ); } else if (vIntensity < 0.05) { // 相对较暗的地方显示之前纹理 gl_FragColor = vec4( outgoingLight, diffuseColor.a ); } else { // 过渡区域按照百分比进行过渡 float t = (vIntensity - 0.05) / 0.16; vec3 fragColor = t*vec3(mapColor.r, mapColor.g, mapColor.b) + (1.0-t) * outgoingLight; gl_FragColor = vec4( fragColor, diffuseColor.a ); }

spotlight光照强度的计算

..src\renderers\shaders\ShaderChunk\lights_fragment_begin.glsl.js

#if ( NUM_SPOT_LIGHTS > 0 ) && defined( RE_Direct )  SpotLight spotLight; #pragma unroll_loop  for ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) {    spotLight = spotLights[ i ];    // getSpotDirectLightIrradiance( spotLight, geometry, directLight );    // 计算光照强度    vIntensity = getSpotDirectLightIntensity( spotLight, geometry, directLight);    getSpotDirectLightIrradiance( spotLight, geometry, directLight ); #ifdef USE_SHADOWMAP directLight.color *= all( bvec2( spotLight.shadow, directLight.visible ) ) ? getShadow( spotShadowMap[ i ], spotLight.shadowMapSize, spotLight.shadowBias, spotLight.shadowRadius, vSpotShadowCoord[ i ] ) : 1.0;    #endif    RE_Direct( directLight, geometry, material, reflectedLight ); }
#endif

光照计算函数:

..src\renderers\shaders\ShaderChunk\lights_pars_begin.glsl.js

// yen 计算强度,参数是spotlight,还有方向光directLight float getSpotDirectLightIntensity( const in SpotLight spotLight, const in GeometricContext geometry, in IncidentLight directLight ) {
float intensity = 0.0; vec3 lVector = spotLight.position - geometry.position; directLight.direction = normalize( lVector );
float lightDistance = length( lVector ); float angleCos = dot( directLight.direction, spotLight.direction );
if ( angleCos > spotLight.coneCos ) { float spotEffect = smoothstep( spotLight.coneCos, spotLight.penumbraCos, angleCos ); intensity = spotEffect * punctualLightIntensityToIrradianceFactor( lightDistance, spotLight.distance, spotLight.decay ); } return intensity; }


这样就大功告成了。

参考文章:

OpenGL ES 3.x游戏开发 上卷 吴亚峰2016 Sample7_5


BIM树洞

做一个静谧的树洞君

用建筑的语言描述IT事物;

用IT的思维解决建筑问题;

共建BIM桥梁,聚合团队。


本学习分享资料不得用于商业用途,仅做学习交流!!如有侵权立即删除!!