提交 c3ab2eb1 编写于 作者: M Mikael Emtinger

Rewrote lens flares to not use readPixels. There are different solutions based...

Rewrote lens flares to not use readPixels. There are different solutions based on availablity of vertex textures. If available, occlusion calculation is more exact and handles partial occlusion on screen edges. If not available, occlusion calculation is less exact (because it's made on each fragment so it has to be fast) and partial occlusion by screen edge is not possible (same reason).

It's not possible for the lens flare to update on visibility any more, only its screen position. Potentially, we could add offset+factor for opacity/scale/rotation that is dependent on visibility in shader. Or just custom shaders.

Anyway, it's way faster than readPixels :)
上级 4470e0f6
...@@ -51,7 +51,7 @@ THREE.LensFlare.prototype.add = function( texture, size, distance, blending ) { ...@@ -51,7 +51,7 @@ THREE.LensFlare.prototype.add = function( texture, size, distance, blending ) {
* Set myLensFlare.customUpdateCallback to alter the flares in your project specific way. * Set myLensFlare.customUpdateCallback to alter the flares in your project specific way.
*/ */
THREE.LensFlare.prototype.updateLensFlares = function( visibility ) { THREE.LensFlare.prototype.updateLensFlares = function() {
var f, fl = this.lensFlares.length; var f, fl = this.lensFlares.length;
var flare; var flare;
...@@ -66,13 +66,8 @@ THREE.LensFlare.prototype.updateLensFlares = function( visibility ) { ...@@ -66,13 +66,8 @@ THREE.LensFlare.prototype.updateLensFlares = function( visibility ) {
flare.x = this.positionScreen.x + vecX * flare.distance; flare.x = this.positionScreen.x + vecX * flare.distance;
flare.y = this.positionScreen.y + vecY * flare.distance; flare.y = this.positionScreen.y + vecY * flare.distance;
flare.wantedScale = ( visibility * 0.2 + 0.8 );
flare.wantedRotation = flare.x * Math.PI * 0.25; flare.wantedRotation = flare.x * Math.PI * 0.25;
flare.wantedOpacity = visibility;
flare.scale += ( flare.wantedScale - flare.scale ) * 0.25;
flare.rotation += ( flare.wantedRotation - flare.rotation ) * 0.25; flare.rotation += ( flare.wantedRotation - flare.rotation ) * 0.25;
flare.opacity += ( flare.wantedOpacity - flare.opacity ) * 0.5;
} }
......
...@@ -161,7 +161,7 @@ THREE.WebGLRenderer = function ( parameters ) { ...@@ -161,7 +161,7 @@ THREE.WebGLRenderer = function ( parameters ) {
_lensFlare.vertexBuffer = _gl.createBuffer(); _lensFlare.vertexBuffer = _gl.createBuffer();
_lensFlare.elementBuffer = _gl.createBuffer(); _lensFlare.elementBuffer = _gl.createBuffer();
_lensFlare.tempTexture = _gl.createTexture(); _lensFlare.tempTexture = _gl.createTexture();
_lensFlare.readBackPixels = new Uint8Array( 16*16*4 ); _lensFlare.occlusionTexture = _gl.createTexture();
_gl.bindBuffer( _gl.ARRAY_BUFFER, _lensFlare.vertexBuffer ); _gl.bindBuffer( _gl.ARRAY_BUFFER, _lensFlare.vertexBuffer );
_gl.bufferData( _gl.ARRAY_BUFFER, _lensFlare.vertices, _gl.STATIC_DRAW ); _gl.bufferData( _gl.ARRAY_BUFFER, _lensFlare.vertices, _gl.STATIC_DRAW );
...@@ -176,23 +176,47 @@ THREE.WebGLRenderer = function ( parameters ) { ...@@ -176,23 +176,47 @@ THREE.WebGLRenderer = function ( parameters ) {
_gl.texParameteri( _gl.TEXTURE_2D, _gl.TEXTURE_MAG_FILTER, _gl.NEAREST ); _gl.texParameteri( _gl.TEXTURE_2D, _gl.TEXTURE_MAG_FILTER, _gl.NEAREST );
_gl.texParameteri( _gl.TEXTURE_2D, _gl.TEXTURE_MIN_FILTER, _gl.NEAREST ); _gl.texParameteri( _gl.TEXTURE_2D, _gl.TEXTURE_MIN_FILTER, _gl.NEAREST );
_lensFlare.program = _gl.createProgram(); _gl.bindTexture( _gl.TEXTURE_2D, _lensFlare.occlusionTexture );
_gl.texImage2D( _gl.TEXTURE_2D, 0, _gl.RGBA, 16, 16, 0, _gl.RGBA, _gl.UNSIGNED_BYTE, null );
_gl.texParameteri( _gl.TEXTURE_2D, _gl.TEXTURE_WRAP_S, _gl.CLAMP_TO_EDGE );
_gl.texParameteri( _gl.TEXTURE_2D, _gl.TEXTURE_WRAP_T, _gl.CLAMP_TO_EDGE );
_gl.texParameteri( _gl.TEXTURE_2D, _gl.TEXTURE_MAG_FILTER, _gl.NEAREST );
_gl.texParameteri( _gl.TEXTURE_2D, _gl.TEXTURE_MIN_FILTER, _gl.NEAREST );
if( _gl.getParameter( _gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS ) <= 0 ) {
_lensFlare.hasVertexTexture = false;
_lensFlare.program = _gl.createProgram();
_gl.attachShader( _lensFlare.program, getShader( "fragment", THREE.ShaderLib.lensFlare.fragmentShader )); _gl.attachShader( _lensFlare.program, getShader( "fragment", THREE.ShaderLib.lensFlare.fragmentShader ));
_gl.attachShader( _lensFlare.program, getShader( "vertex", THREE.ShaderLib.lensFlare.vertexShader )); _gl.attachShader( _lensFlare.program, getShader( "vertex", THREE.ShaderLib.lensFlare.vertexShader ));
_gl.linkProgram( _lensFlare.program );
} else {
_lensFlare.hasVertexTexture = true;
_lensFlare.program = _gl.createProgram();
_gl.attachShader( _lensFlare.program, getShader( "fragment", THREE.ShaderLib.lensFlareVertexTexture.fragmentShader ));
_gl.attachShader( _lensFlare.program, getShader( "vertex", THREE.ShaderLib.lensFlareVertexTexture.vertexShader ));
_gl.linkProgram( _lensFlare.program ); _gl.linkProgram( _lensFlare.program );
}
_lensFlare.attributes = {}; _lensFlare.attributes = {};
_lensFlare.uniforms = {}; _lensFlare.uniforms = {};
_lensFlare.attributes.vertex = _gl.getAttribLocation ( _lensFlare.program, "position" ); _lensFlare.attributes.vertex = _gl.getAttribLocation ( _lensFlare.program, "position" );
_lensFlare.attributes.uv = _gl.getAttribLocation ( _lensFlare.program, "UV" ); _lensFlare.attributes.uv = _gl.getAttribLocation ( _lensFlare.program, "UV" );
_lensFlare.uniforms.renderType = _gl.getUniformLocation( _lensFlare.program, "renderType" );
_lensFlare.uniforms.map = _gl.getUniformLocation( _lensFlare.program, "map" ); _lensFlare.uniforms.map = _gl.getUniformLocation( _lensFlare.program, "map" );
_lensFlare.uniforms.occlusionMap = _gl.getUniformLocation( _lensFlare.program, "occlusionMap" );
_lensFlare.uniforms.opacity = _gl.getUniformLocation( _lensFlare.program, "opacity" ); _lensFlare.uniforms.opacity = _gl.getUniformLocation( _lensFlare.program, "opacity" );
_lensFlare.uniforms.scale = _gl.getUniformLocation( _lensFlare.program, "scale" ); _lensFlare.uniforms.scale = _gl.getUniformLocation( _lensFlare.program, "scale" );
_lensFlare.uniforms.rotation = _gl.getUniformLocation( _lensFlare.program, "rotation" ); _lensFlare.uniforms.rotation = _gl.getUniformLocation( _lensFlare.program, "rotation" );
_lensFlare.uniforms.screenPosition = _gl.getUniformLocation( _lensFlare.program, "screenPosition" ); _lensFlare.uniforms.screenPosition = _gl.getUniformLocation( _lensFlare.program, "screenPosition" );
_lensFlare.uniforms.renderPink = _gl.getUniformLocation( _lensFlare.program, "renderPink" );
this.setSize = function ( width, height ) { this.setSize = function ( width, height ) {
...@@ -3175,6 +3199,7 @@ THREE.WebGLRenderer = function ( parameters ) { ...@@ -3175,6 +3199,7 @@ THREE.WebGLRenderer = function ( parameters ) {
} }
/* /*
* Render lens flares * Render lens flares
* Method: renders 16x16 0xff00ff-colored points scattered over the light source area, * Method: renders 16x16 0xff00ff-colored points scattered over the light source area,
...@@ -3215,24 +3240,24 @@ THREE.WebGLRenderer = function ( parameters ) { ...@@ -3215,24 +3240,24 @@ THREE.WebGLRenderer = function ( parameters ) {
// loop through all lens flares to update their occlusion and positions // loop through all lens flares to update their occlusion and positions
// setup gl and common used attribs/unforms // setup gl and common used attribs/unforms
_gl.uniform1i( uniforms.map, 0 ); _gl.uniform1i( uniforms.occlusionMap, 0 );
_gl.activeTexture( _gl.TEXTURE0 ); _gl.uniform1i( uniforms.map, 1 );
_gl.uniform1f( uniforms.opacity, 1 );
_gl.uniform1f( uniforms.rotation, 0 );
_gl.uniform2fv( uniforms.scale, scale );
_gl.bindBuffer( _gl.ARRAY_BUFFER, _lensFlare.vertexBuffer ); _gl.bindBuffer( _gl.ARRAY_BUFFER, _lensFlare.vertexBuffer );
_gl.vertexAttribPointer( attributes.vertex, 2, _gl.FLOAT, false, 2 * 8, 0 ); _gl.vertexAttribPointer( attributes.vertex, 2, _gl.FLOAT, false, 2 * 8, 0 );
_gl.vertexAttribPointer( attributes.uv, 2, _gl.FLOAT, false, 2 * 8, 8 ); _gl.vertexAttribPointer( attributes.uv, 2, _gl.FLOAT, false, 2 * 8, 8 );
_gl.bindTexture( _gl.TEXTURE_2D, _lensFlare.tempTexture );
_gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, _lensFlare.elementBuffer ); _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, _lensFlare.elementBuffer );
_gl.disable( _gl.CULL_FACE ); _gl.disable( _gl.CULL_FACE );
_gl.depthMask( false ); _gl.depthMask( false );
_gl.activeTexture( _gl.TEXTURE0 );
_gl.bindTexture( _gl.TEXTURE_2D, _lensFlare.occlusionTexture );
_gl.activeTexture( _gl.TEXTURE1 );
_gl.bindTexture( _gl.TEXTURE_2D, _lensFlare.tempTexture );
for ( o = 0; o < ol; o ++ ) { for ( o = 0; o < ol; o ++ ) {
...@@ -3257,73 +3282,48 @@ THREE.WebGLRenderer = function ( parameters ) { ...@@ -3257,73 +3282,48 @@ THREE.WebGLRenderer = function ( parameters ) {
screenPositionPixels[ 1 ] = screenPosition[ 1 ] * halfViewportHeight + halfViewportHeight; screenPositionPixels[ 1 ] = screenPosition[ 1 ] * halfViewportHeight + halfViewportHeight;
// screen cull or go on if vertex texture
if( _lensFlare.hasVertexTexture ||
( screenPositionPixels[ 0 ] > 0 &&
screenPositionPixels[ 0 ] < _viewportWidth &&
screenPositionPixels[ 1 ] > 0 &&
screenPositionPixels[ 1 ] < _viewportHeight )) {
// save current RGB to temp texture // save current RGB to temp texture
_gl.bindTexture( _gl.TEXTURE_2D, _lensFlare.tempTexture );
_gl.copyTexSubImage2D( _gl.TEXTURE_2D, 0, 0, 0, screenPositionPixels[ 0 ] - 8, screenPositionPixels[ 1 ] - 8, 16, 16 ); _gl.copyTexSubImage2D( _gl.TEXTURE_2D, 0, 0, 0, screenPositionPixels[ 0 ] - 8, screenPositionPixels[ 1 ] - 8, 16, 16 );
// render pink quad // render pink quad
_gl.uniform1i( uniforms.renderType, 0 );
_gl.uniform2fv( uniforms.scale, scale );
_gl.uniform3fv( uniforms.screenPosition, screenPosition ); _gl.uniform3fv( uniforms.screenPosition, screenPosition );
_gl.uniform1i( uniforms.renderPink, 1 );
_gl.disable( _gl.BLEND );
_gl.enable( _gl.DEPTH_TEST ); _gl.enable( _gl.DEPTH_TEST );
_gl.drawElements( _gl.TRIANGLES, 6, _gl.UNSIGNED_SHORT, 0 ); _gl.drawElements( _gl.TRIANGLES, 6, _gl.UNSIGNED_SHORT, 0 );
// read back // copy result to occlusionMap
try {
_gl.readPixels( screenPositionPixels[ 0 ] - 8, screenPositionPixels[ 1 ] - 8, 16, 16, _gl.RGBA, _gl.UNSIGNED_BYTE, _lensFlare.readBackPixels );
} catch( error ) {
console.log( "WebGLRenderer.renderLensFlare: readPixels failed!" ); _gl.bindTexture( _gl.TEXTURE_2D, _lensFlare.occlusionTexture );
_gl.copyTexSubImage2D( _gl.TEXTURE_2D, 0, 0, 0, screenPositionPixels[ 0 ] - 8, screenPositionPixels[ 1 ] - 8, 16, 16 );
}
if ( _gl.getError() ) {
console.log( "WebGLRenderer.renderLensFlare: readPixels failed!" );
}
// sample read back pixels
sampleDistance = parseInt( 5 * ( 1 - Math.max( 0, Math.min( -objectZ, camera.far )) / camera.far ), 10 ) + 2;
sampleX = sampleDistance * 4;
sampleY = sampleDistance * 4 * 16;
visibility = 0;
sampleIndex = ( sampleMidX - sampleX ) + ( sampleMidY - sampleY ); // upper left
if( _lensFlare.readBackPixels[ sampleIndex + 0 ] === 255 &&
_lensFlare.readBackPixels[ sampleIndex + 1 ] === 0 &&
_lensFlare.readBackPixels[ sampleIndex + 2 ] === 255 ) visibility += 0.2;
sampleIndex = ( sampleMidX + sampleX ) + ( sampleMidY - sampleY ); // upper right
if( readBackPixels[ sampleIndex + 0 ] === 255 &&
readBackPixels[ sampleIndex + 1 ] === 0 &&
readBackPixels[ sampleIndex + 2 ] === 255 ) visibility += 0.2;
sampleIndex = ( sampleMidX + sampleX ) + ( sampleMidY + sampleY ); // lower right // restore graphics
if( readBackPixels[ sampleIndex + 0 ] === 255 &&
readBackPixels[ sampleIndex + 1 ] === 0 &&
readBackPixels[ sampleIndex + 2 ] === 255 ) visibility += 0.2;
sampleIndex = ( sampleMidX - sampleX ) + ( sampleMidY + sampleY ); // lower left _gl.uniform1i( uniforms.renderType, 1 );
if( readBackPixels[ sampleIndex + 0 ] === 255 && _gl.disable( _gl.DEPTH_TEST );
readBackPixels[ sampleIndex + 1 ] === 0 && _gl.bindTexture( _gl.TEXTURE_2D, _lensFlare.tempTexture );
readBackPixels[ sampleIndex + 2 ] === 255 ) visibility += 0.2; _gl.drawElements( _gl.TRIANGLES, 6, _gl.UNSIGNED_SHORT, 0 );
sampleIndex = sampleMidX + sampleMidY; // center
if( readBackPixels[ sampleIndex + 0 ] === 255 &&
readBackPixels[ sampleIndex + 1 ] === 0 &&
readBackPixels[ sampleIndex + 2 ] === 255 ) visibility += 0.2;
// update object positions
object.positionScreen.x = screenPosition[ 0 ]; object.positionScreen.x = screenPosition[ 0 ];
object.positionScreen.y = screenPosition[ 1 ]; object.positionScreen.y = screenPosition[ 1 ];
...@@ -3331,30 +3331,19 @@ THREE.WebGLRenderer = function ( parameters ) { ...@@ -3331,30 +3331,19 @@ THREE.WebGLRenderer = function ( parameters ) {
if ( object.customUpdateCallback ) { if ( object.customUpdateCallback ) {
object.customUpdateCallback( visibility, object ); object.customUpdateCallback( object );
} else { } else {
object.updateLensFlares( visibility ); object.updateLensFlares();
} }
// restore graphics // render flares
_gl.uniform1i( uniforms.renderPink, 0 ); _gl.uniform1i( uniforms.renderType, 2 );
_gl.disable( _gl.DEPTH_TEST ); _gl.enable( _gl.BLEND );
_gl.drawElements( _gl.TRIANGLES, 6, _gl.UNSIGNED_SHORT, 0 );
}
// loop through all lens flares and draw their flares
// setup gl
for ( o = 0; o < ol; o ++ ) {
object = scene.__webglLensFlares[ o ].object;
for ( f = 0, fl = object.lensFlares.length; f < fl; f ++ ) { for ( f = 0, fl = object.lensFlares.length; f < fl; f ++ ) {
...@@ -3371,14 +3360,12 @@ THREE.WebGLRenderer = function ( parameters ) { ...@@ -3371,14 +3360,12 @@ THREE.WebGLRenderer = function ( parameters ) {
scale[ 1 ] = size; scale[ 1 ] = size;
_gl.uniform3fv( uniforms.screenPosition, screenPosition ); _gl.uniform3fv( uniforms.screenPosition, screenPosition );
_gl.uniform1f( uniforms.rotation, flare.rotation );
_gl.uniform2fv( uniforms.scale, scale ); _gl.uniform2fv( uniforms.scale, scale );
_gl.uniform1f( uniforms.rotation, flare.rotation );
_gl.uniform1f( uniforms.opacity, flare.opacity ); _gl.uniform1f( uniforms.opacity, flare.opacity );
setBlending( flare.blending ); setBlending( flare.blending );
setTexture( flare.texture, 0 ); setTexture( flare.texture, 1 );
// todo: only draw if loaded
_gl.drawElements( _gl.TRIANGLES, 6, _gl.UNSIGNED_SHORT, 0 ); _gl.drawElements( _gl.TRIANGLES, 6, _gl.UNSIGNED_SHORT, 0 );
...@@ -3388,6 +3375,8 @@ THREE.WebGLRenderer = function ( parameters ) { ...@@ -3388,6 +3375,8 @@ THREE.WebGLRenderer = function ( parameters ) {
} }
}
// restore gl // restore gl
_gl.enable( _gl.CULL_FACE ); _gl.enable( _gl.CULL_FACE );
...@@ -3397,6 +3386,8 @@ THREE.WebGLRenderer = function ( parameters ) { ...@@ -3397,6 +3386,8 @@ THREE.WebGLRenderer = function ( parameters ) {
} }
function setupMatrices( object, camera ) { function setupMatrices( object, camera ) {
object._modelViewMatrix.multiplyToArray( camera.matrixWorldInverse, object.matrixWorld, object._modelViewMatrixArray ); object._modelViewMatrix.multiplyToArray( camera.matrixWorldInverse, object.matrixWorld, object._modelViewMatrixArray );
......
...@@ -569,6 +569,95 @@ THREE.UniformsLib = { ...@@ -569,6 +569,95 @@ THREE.UniformsLib = {
THREE.ShaderLib = { THREE.ShaderLib = {
'lensFlareVertexTexture': {
vertexShader: [
"uniform vec3 screenPosition;",
"uniform vec2 scale;",
"uniform float rotation;",
"uniform int renderType;",
"uniform sampler2D occlusionMap;",
"attribute vec2 position;",
"attribute vec2 UV;",
"varying vec2 vUV;",
"varying float vVisibility;",
"void main(void)",
"{",
"vUV = UV;",
"vec2 pos = position;",
"if( renderType == 2 ) {",
"vec4 visibility = texture2D( occlusionMap, vec2( 0.1, 0.1 )) +",
"texture2D( occlusionMap, vec2( 0.5, 0.1 )) +",
"texture2D( occlusionMap, vec2( 0.9, 0.1 )) +",
"texture2D( occlusionMap, vec2( 0.9, 0.5 )) +",
"texture2D( occlusionMap, vec2( 0.9, 0.9 )) +",
"texture2D( occlusionMap, vec2( 0.5, 0.9 )) +",
"texture2D( occlusionMap, vec2( 0.1, 0.9 )) +",
"texture2D( occlusionMap, vec2( 0.1, 0.5 )) +",
"texture2D( occlusionMap, vec2( 0.5, 0.5 ));",
"vVisibility = ( visibility.r / 9.0 ) *",
"( 1.0 - visibility.g / 9.0 ) *",
"( visibility.b / 9.0 ) *",
"( 1.0 - visibility.a / 9.0 );",
"pos.x = cos( rotation ) * position.x - sin( rotation ) * position.y;",
"pos.y = sin( rotation ) * position.x + cos( rotation ) * position.y;",
"}",
"gl_Position = vec4(( pos * scale + screenPosition.xy ).xy, screenPosition.z, 1.0 );",
"}"
].join( "\n" ),
fragmentShader: [
"#ifdef GL_ES",
"precision highp float;",
"#endif",
"uniform sampler2D map;",
"uniform float opacity;",
"uniform int renderType;",
"varying vec2 vUV;",
"varying float vVisibility;",
"void main( void )",
"{",
// pink square
"if( renderType == 0 ) {",
"gl_FragColor = vec4( 1.0, 0.0, 1.0, 0.0 );",
// restore
"} else if( renderType == 1 ) {",
"gl_FragColor = texture2D( map, vUV );",
// flare
"} else {",
"vec4 color = texture2D( map, vUV );",
"color.a *= opacity * vVisibility;",
"gl_FragColor = color;",
"}",
"}"
].join( "\n" )
},
'lensFlare': { 'lensFlare': {
vertexShader: [ vertexShader: [
...@@ -576,17 +665,25 @@ THREE.ShaderLib = { ...@@ -576,17 +665,25 @@ THREE.ShaderLib = {
"uniform vec3 screenPosition;", "uniform vec3 screenPosition;",
"uniform vec2 scale;", "uniform vec2 scale;",
"uniform float rotation;", "uniform float rotation;",
"uniform int renderType;",
"attribute vec2 position;", "attribute vec2 position;",
"attribute vec2 UV;", "attribute vec2 UV;",
"varying vec2 vUV;", "varying vec2 vUV;",
"void main(void)", "void main(void)",
"{", "{",
"vUV = UV;", "vUV = UV;",
"vec2 pos;", "vec2 pos = position;",
"if( renderType == 2 ) {",
"pos.x = cos( rotation ) * position.x - sin( rotation ) * position.y;", "pos.x = cos( rotation ) * position.x - sin( rotation ) * position.y;",
"pos.y = sin( rotation ) * position.x + cos( rotation ) * position.y;", "pos.y = sin( rotation ) * position.x + cos( rotation ) * position.y;",
"}",
"gl_Position = vec4(( pos * scale + screenPosition.xy ).xy, screenPosition.z, 1.0 );", "gl_Position = vec4(( pos * scale + screenPosition.xy ).xy, screenPosition.z, 1.0 );",
"}" "}"
...@@ -599,18 +696,39 @@ THREE.ShaderLib = { ...@@ -599,18 +696,39 @@ THREE.ShaderLib = {
"#endif", "#endif",
"uniform sampler2D map;", "uniform sampler2D map;",
"uniform sampler2D occlusionMap;",
"uniform float opacity;", "uniform float opacity;",
"uniform int renderType;",
"uniform int renderPink;",
"varying vec2 vUV;", "varying vec2 vUV;",
"void main( void )", "void main( void )",
"{", "{",
"if( renderPink == 1 ) {", // pink square
"gl_FragColor = vec4( 1.0, 0.0, 1.0, 1.0 );",
"if( renderType == 0 ) {",
"gl_FragColor = vec4( 1.0, 0.0, 1.0, 0.0 );",
// restore
"} else if( renderType == 1 ) {",
"gl_FragColor = texture2D( map, vUV );",
// flare
"} else {", "} else {",
"float visibility = texture2D( occlusionMap, vec2( 0.5, 0.1 )).a +",
"texture2D( occlusionMap, vec2( 0.9, 0.5 )).a +",
"texture2D( occlusionMap, vec2( 0.5, 0.9 )).a +",
"texture2D( occlusionMap, vec2( 0.1, 0.5 )).a;",
"visibility = ( 1.0 - visibility / 4.0 );",
"vec4 color = texture2D( map, vUV );", "vec4 color = texture2D( map, vUV );",
"color.a *= opacity;", "color.a *= opacity * visibility;",
"gl_FragColor = color;", "gl_FragColor = color;",
"}", "}",
"}" "}"
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册