someGraphicObject.filters = [filter]
Scene (PIXI.Container) | - Lightbulbs (PIXI.Graphics pair) | - Sprite on which shadows will be drawn + lighted background (PIXI.Sprite) | - | - Texture (PIXI.RenderTexture) | - | - Filter (PIXI.AbstractFilter)
| - Background (PIXI.Sprite) | - | - Texture Background | - Obstacle Container (PIXI.Container) | - | - Sprite with obstacles (PIXI.Sprite) | - | - | - Sprite texture
<pre> <!DOCTYPE HTML> <html> <head> <title>Deferred Example</title> <meta charset="UTF-8"> <style></style> <script src="js/pixi.min.js"></script> <script src="js/script.js"></script> </head> <body> </body> </html>
(function () { var WIDTH = 640; var HEIGHT = 480; var renderer = new PIXI.WebGLRenderer(WIDTH, HEIGHT); // PIXI document.addEventListener("DOMContentLoaded", function (event) { // html document.body.appendChild(renderer.view); // PIXI.loader .add('background', 'img/maze.png') .once('complete', setup) .load(); }); function setup() { // , . var lights = []; // lights[0] = new PIXI.Graphics(); lights[0].beginFill(0xFFFF00); lights[0].drawCircle(0, 0, 4); // x, y, radius lights[1] = new PIXI.Graphics(); lights[1].beginFill(0xFFFF00); lights[1].drawCircle(0, 0, 4); lights[1].x = 50; lights[1].y = 50; var background = new PIXI.Graphics(); background.beginFill(0x999999); background.drawRect(0, 0, WIDTH, HEIGHT); // x, y, width, height var shadowCastImage = PIXI.Sprite.fromImage('img/maze.png'); // () var shadowCasters = new PIXI.Container(); // , shadowCasters.addChild(shadowCastImage); // . var stage = new PIXI.Container(); stage.addChild(background); stage.addChild(shadowCasters); stage.addChild(lights[0]); stage.addChild(lights[1]); (function animate() { // lights[0] . var pointer = renderer.plugins.interaction.mouse.global; lights[0].x = pointer.x; lights[0].y = pointer.y; // renderer.render(stage); requestAnimationFrame(animate); })(); } })();
precision mediump float; // varying vec2 vTextureCoord; // ( 0.0 1.0) uniform sampler2D uSampler; // , . void main() { vec4 color = texture2D(uSampler, vTextureCoord); // if (color.a == 0.) { // - gl_FragColor = vec4(.5, .5, .5, 1.); // , } else { // gl_FragColor = vec4(1., 0., 0., 1.); // , } }
PIXI.loader ... .add('glslShadowTexture', 'glsl/smap-shadow-texture.frag') ...
var lightingRT = new PIXI.RenderTexture(renderer, WIDTH, HEIGHT); var lightingSprite = new PIXI.Sprite(lightingRT); var filter = createSMapFilter(); lightingSprite.filters = [filter]; ... stage.addChild(lightingSprite);
lightingRT.render(shadowCasters, null, true); // (shadowCasters PIXI, null (), true )
function createSMapFilter() { var SMapFilter = new PIXI.AbstractFilter(null, PIXI.loader.resources.glslShadowTexture.data, {}); // ( , , (uniforms)) return SMapFilter; }
function createSMapFilter() { var CONST_LIGHTS_COUNT = 2; var SMapFilter = new PIXI.AbstractFilter(null, null, { // , , "" viewResolution: {type: '2fv', value: [WIDTH, HEIGHT]} // view , rtSize: {type: '2fv', value: [1024, CONST_LIGHTS_COUNT]} // . , uAmbient: {type: '4fv', value: [.0, .0, .0, .0]} // " " . }); // / : for (var i = 0; i < CONST_LIGHTS_COUNT; ++i) { SMapFilter.uniforms['uLightPosition[' + i + ']'] = {type: '4fv', value: [0, 0, 256, .3]}; // , , "falloff" SMapFilter.uniforms['uLightColor[' + i + ']'] = {type: '4fv', value: [1, 1, 1, .3]}; // r, g, b, . } // PIXI , SMapFilter.renderTarget = new PIXI.RenderTarget( renderer.gl , SMapFilter.uniforms.rtSize.value[0] , SMapFilter.uniforms.rtSize.value[1] , PIXI.SCALE_MODES.LINEAR , 1); SMapFilter.renderTarget.transform = new PIXI.Matrix() .scale(SMapFilter.uniforms.rtSize.value[0] / WIDTH , SMapFilter.uniforms.rtSize.value[1] / HEIGHT); // : SMapFilter.shadowCastersRT = new PIXI.RenderTexture(renderer, WIDTH, HEIGHT); SMapFilter.uniforms.uShadowCastersTexture = { type: 'sampler2D', value: SMapFilter.shadowCastersRT }; // : SMapFilter.render = function (group) { SMapFilter.shadowCastersRT.render(group, null, true); }; // , ; SMapFilter.testFilter = new PIXI.AbstractFilter(null, "precision highp float;" + "varying vec2 vTextureCoord;" + "uniform sampler2D uSampler;" + "void main(void) {gl_FragColor = texture2D(uSampler, vTextureCoord);}"); // , renderTarget . var filterShadowTextureSource = PIXI.loader.resources.glslShadowTexture.data; // CONST_LIGHTS_COUNT uniform. filterShadowTextureSource = filterShadowTextureSource.replace(/CONST_LIGHTS_COUNT/g, CONST_LIGHTS_COUNT); // uniforms, WebGL ( , ) var filterShadowTextureUniforms = Object.keys(SMapFilter.uniforms).reduce(function (c, k) { c[k] = { type: SMapFilter.uniforms[k].type , value: SMapFilter.uniforms[k].value }; return c; }, {}); SMapFilter.filterShadowTexture = new PIXI.AbstractFilter( null , filterShadowTextureSource , filterShadowTextureUniforms ); SMapFilter.applyFilter = function (renderer, input, output) { SMapFilter.filterShadowTexture.applyFilter(renderer, input, SMapFilter.renderTarget, true); SMapFilter.testFilter.applyFilter(renderer, SMapFilter.renderTarget, output); // . }; return SMapFilter; }
// uniforms filter.uniforms['uLightPosition[0]'].value[0] = lights[0].x; filter.uniforms['uLightPosition[0]'].value[1] = lights[0].y; filter.uniforms['uLightPosition[1]'].value[0] = lights[1].x; filter.uniforms['uLightPosition[1]'].value[1] = lights[1].y; // renderTexture . filter.render(shadowCasters);
// : precision mediump float; // uniform' varying vec2 vTextureCoord; // uniform sampler2D uSampler; // ( ) uniform vec2 viewResolution; // uniform vec2 rtSize; // renderTarget uniform vec4 uLightPosition[CONST_LIGHTS_COUNT]; //x,y = , z = uniform vec4 uLightColor[CONST_LIGHTS_COUNT]; // uniform sampler2D uShadowCastersTexture; // -- . const float PI = 3.14159265358979; const float STEPS = 256.0; const float THRESHOLD = .01; void main(void) { int lightnum = int(floor(vTextureCoord.y * float(CONST_LIGHTS_COUNT))); // Y vec2 lightPosition; float lightSize; for (int i = 0; i < CONST_LIGHTS_COUNT; i += 1) { // if (lightnum == i) { lightPosition = uLightPosition[i].xy / viewResolution; lightSize = uLightPosition[i].z / max(viewResolution.x, viewResolution.y); break; } } float dst = 1.0; // for (float y = 0.0; y < STEPS; y += 1.0) { // ( (y / STEPS)) float distance = (y / STEPS); // float angle = vTextureCoord.x * (2.0 * PI); // // vec2 coord = vec2(cos(angle) * distance, sin(angle) * distance); coord *= (max(viewResolution.x, viewResolution.y) / viewResolution); // coord += lightPosition; // coord = clamp(coord, 0., 1.); // vec4 data = texture2D(uShadowCastersTexture, coord); // if (data.a > THRESHOLD) { // , . dst = min(dst, distance); break; } } // , 0..1 gl_FragColor = vec4(vec3(0.0), dst / lightSize); }
PIXI.loader .add('background', 'img/maze.png') .add('glslShadowTexture', 'glsl/smap-shadow-texture.frag') .add('glslShadowCast', 'glsl/smap-shadow-cast.frag') .once('complete', setup) .load();
var filterShadowCastUniforms = Object.keys(SMapFilter.uniforms).reduce(function (c, k) { c[k] = { type: SMapFilter.uniforms[k].type , value: SMapFilter.uniforms[k].value }; return c; }, {});
filterShadowCastUniforms.shadowMapChannel = { type: 'sampler2D', value: { baseTexture: { hasLoaded: true , _glTextures: [SMapFilter.renderTarget.texture] } } };
SMapFilter.filterShadowCast = new PIXI.AbstractFilter( null , PIXI.loader.resources.glslShadowCast.data.replace(/CONST_LIGHTS_COUNT/g, CONST_LIGHTS_COUNT) , filterShadowCastUniforms );
SMapFilter.applyFilter = function (renderer, input, output) { SMapFilter.filterShadowTexture.applyFilter(renderer, input, SMapFilter.renderTarget, true); //SMapFilter.testFilter.applyFilter(renderer, SMapFilter.renderTarget, output); // . SMapFilter.filterShadowCast.applyFilter(renderer, input, output); };
precision mediump float; uniform sampler2D uSampler; varying vec2 vTextureCoord; uniform vec2 viewResolution; uniform sampler2D shadowMapChannel; uniform vec4 uAmbient; uniform vec4 uLightPosition[CONST_LIGHTS_COUNT]; uniform vec4 uLightColor[CONST_LIGHTS_COUNT]; const float PI = 3.14159265358979; // () vec4 takeSample(in sampler2D texture, in vec2 coord, in float light) { return step(light, texture2D(texture, coord)); } // ( !) vec4 blurFn(in sampler2D texture, in vec2 tc, in float light, in float iBlur) { float blur = iBlur / viewResolution.x; vec4 sum = vec4(0.0); sum += takeSample(texture, vec2(tc.x - 5.0*blur, tc.y), light) * 0.022657; sum += takeSample(texture, vec2(tc.x - 4.0*blur, tc.y), light) * 0.046108; sum += takeSample(texture, vec2(tc.x - 3.0*blur, tc.y), light) * 0.080127; sum += takeSample(texture, vec2(tc.x - 2.0*blur, tc.y), light) * 0.118904; sum += takeSample(texture, vec2(tc.x - 1.0*blur, tc.y), light) * 0.150677; sum += takeSample(texture, vec2(tc.x, tc.y), light) * 0.163053; sum += takeSample(texture, vec2(tc.x + 1.0*blur, tc.y), light) * 0.150677; sum += takeSample(texture, vec2(tc.x + 2.0*blur, tc.y), light) * 0.118904; sum += takeSample(texture, vec2(tc.x + 3.0*blur, tc.y), light) * 0.080127; sum += takeSample(texture, vec2(tc.x + 4.0*blur, tc.y), light) * 0.046108; sum += takeSample(texture, vec2(tc.x + 5.0*blur, tc.y), light) * 0.022657; return sum; } // , : void main() { // vec4 color = vec4(0.0, 0.0, 0.0, 1.0); // . float lightLookupHalfStep = (1.0 / float(CONST_LIGHTS_COUNT)) * .5; // for (int lightNumber = 0; lightNumber < CONST_LIGHTS_COUNT; lightNumber += 1) { float lightSize = uLightPosition[lightNumber].z / max(viewResolution.x, viewResolution.y); float lightFalloff = min(0.99, uLightPosition[lightNumber].a); if (lightSize == 0.) { // , . continue; } vec2 lightPosition = uLightPosition[lightNumber].xy / viewResolution; vec4 lightColor = uLightColor[lightNumber]; // vec3 lightLuminosity = vec3(0.0); // Y . float yCoord = float(lightNumber) / float(CONST_LIGHTS_COUNT) + lightLookupHalfStep; // vec2 toLight = vTextureCoord - lightPosition; // toLight /= (max(viewResolution.x, viewResolution.y) / viewResolution); toLight /= lightSize; // float light = length(toLight); // ( ) float angleToPoint = atan(toLight.y, toLight.x); float angleCoordOnMap = angleToPoint / (2.0 * PI); vec2 samplePoint = vec2(angleCoordOnMap, yCoord); // -- . float blur = smoothstep(0., 2., light); // , float sum = blurFn(shadowMapChannel, samplePoint, light, blur).a; sum = max(sum, lightColor.a); lightLuminosity = lightColor.rgb * vec3(sum) * smoothstep(1.0, lightFalloff, light); // ( ): color.rgb += lightLuminosity; } // color = max(color, uAmbient); // vec4 base = texture2D(uSampler, vTextureCoord); // "" ( ) gl_FragColor = vec4(base.rgb * sqrt(color.rgb), 1.0); }
Source: https://habr.com/ru/post/272233/
All Articles