ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

three.js 火焰效果

2021-11-03 21:33:43  阅读:388  来源: 互联网

标签:life geometry float THREE three 火焰 var new js


代码是网上找的代码,通过调参、修改、封装实现的。

代码:

/**
 * 火焰
 */

import * as THREE from '../build/three.module.js';

let MyFire3 = function () {

    let fireVertexShader = `
attribute vec4 orientation;
attribute vec3 offset;
attribute vec2 scale;
attribute float life;
attribute float random;

varying vec2 vUv;
varying float vRandom;
varying float vAlpha;

float range(float oldValue, float oldMin, float oldMax, float newMin, float newMax) {
    float oldRange = oldMax - oldMin;
    float newRange = newMax - newMin;
    return (((oldValue - oldMin) * newRange) / oldRange) + newMin;
}

// From Inigo Quilez http://www.iquilezles.org/www/articles/functions/functions.htm
float pcurve(float x, float a, float b) {
    float k = pow(a + b, a + b) / (pow(a, a) * pow(b, b));
    return k * pow(x, a) * pow(1.0 - x, b);
}

void main() {
    vUv = uv;
    vRandom = random;

    vAlpha = pcurve(life, 1.0, 2.0);

    vec3 pos = position;

    pos.xy *= scale * vec2(range(pow(life, 1.5), 0.0, 1.0, 1.0, 0.6), range(pow(life, 1.5), 0.0, 1.0, 0.6, 1.2));

    vec4 or = orientation;
    vec3 vcV = cross(or.xyz, pos);
    pos = vcV * (2.0 * or.w) + (cross(or.xyz, vcV) * 2.0 + pos);

    gl_Position = projectionMatrix * modelViewMatrix * vec4(pos, 1.0);
}
`;

    let fireFragmentShader = `
uniform sampler2D uMap;
uniform vec3 uColor1;
uniform vec3 uColor2;
uniform float uTime;

varying vec2 vUv;
varying float vAlpha;
varying float vRandom;

void main() {
    vec2 uv = vUv;

    float spriteLength = 10.0;
    uv.x /= spriteLength;
    float spriteIndex = mod(uTime * 0.1 + vRandom * 2.0, 1.0);
    uv.x += floor(spriteIndex * spriteLength) / spriteLength;

    vec4 map = texture2D(uMap, uv);

    gl_FragColor.rgb = mix(uColor2, uColor1, map.r);
    gl_FragColor.a = vAlpha * map.a;
}
`;

    let embersVertexShader = `
attribute float size;
attribute float life;
attribute vec3 offset;

varying float vAlpha;

// From Inigo Quilez http://www.iquilezles.org/www/articles/functions/functions.htm
float impulse(float k, float x) {
    float h = k * x;
    return h * exp(1.0 - h);
}

void main() {
    vAlpha = impulse(6.28, life);

    vec3 pos = position;
    pos += offset * vec3(life * 0.7 + 0.3, life * 0.9 + 0.1, life * 0.7 + 0.3);

    vec4 mvPosition = modelViewMatrix * vec4(pos, 1.0);
    gl_PointSize = size * (80.0 / length(mvPosition.xyz));
    gl_Position = projectionMatrix * mvPosition;
}
`;

    let embersFragmentShader = `
uniform sampler2D uMap;
uniform vec3 uColor;

varying float vAlpha;

void main() {
    vec2 uv = vec2(gl_PointCoord.x, 1.0 - gl_PointCoord.y);
    vec4 mask = texture2D(uMap, uv);

    gl_FragColor.rgb = uColor;
    gl_FragColor.a = mask.a * vAlpha * 0.8;
}
`;

    let hazeVertexShader = `
attribute vec3 base;
attribute vec3 offset;
attribute vec4 orientation;
attribute vec2 scale;
attribute float life;

varying float vAlpha;
varying vec2 vUv;

// From Inigo Quilez http://www.iquilezles.org/www/articles/functions/functions.htm
float impulse(float k, float x) {
    float h = k * x;
    return h * exp(1.0 - h);
}

float pcurve(float x, float a, float b) {
    float k = pow(a + b, a + b) / (pow(a, a) * pow(b, b));
    return k * pow(x, a) * pow(1.0 - x, b);
}

void main() {
    vUv = uv;
    vAlpha = pcurve(life, 1.0, 2.0);

    vec3 pos = position;

    pos.xy *= scale * (life * 0.7 + 0.3);

    vec4 or = orientation;
    vec3 vcV = cross(or.xyz, pos);
    pos = vcV * (2.0 * or.w) + (cross(or.xyz, vcV) * 2.0 + pos);

    pos += base;
    pos += offset * vec3(life * 0.7 + 0.3, life * 0.9 + 0.1, life * 0.7 + 0.3);

    gl_Position = projectionMatrix * modelViewMatrix * vec4(pos, 1.0);;
}
`;

    let hazeFragmentShader = `
uniform sampler2D uMap;
uniform sampler2D uMask;
uniform vec2 uResolution;

varying float vAlpha;
varying vec2 vUv;

void main() {
    vec2 uv = gl_FragCoord.xy / uResolution;
    vec2 mask = texture2D(uMask, vUv).ra - vec2(0.5);
    uv -= mask * 0.1;
    vec4 tex = texture2D(uMap, uv);

    gl_FragColor.rgb = tex.rgb;
    gl_FragColor.a = vAlpha * 0.5;
}
`;

    function random(min, max, precision) {
        var p = Math.pow(10, precision);
        return Math.round((min + Math.random() * (max - min)) * p) / p;
    }


    let _scene;
    let _renderer;
    let _camera;
    let _controls;
    let _rtt;
    let _fire;
    let _width;
    let _height;

    this.objs = [];

    let _self = this;

    this._isShow = false;

    let _pos_x;
    let _pos_y;
    let _pos_z;

    this.config = function (scene_, renderer_, camera_, controls_) {
        _width = 1920;
        _height = 1040;

        _renderer = renderer_;
        _scene = scene_;
        _camera = camera_;
        _controls = controls_;

        var _parameters = {
            minFilter: THREE.LinearFilter,
            magFilter: THREE.LinearFilter,
            format: THREE.RGBAFormat,
            stencilBuffer: false
        };
        _rtt = new THREE.WebGLRenderTarget(_width * 0.5, _height * 0.5, _parameters);
    }

    this.setPosition = function name(x, y, z) {
        _pos_x = x;
        _pos_y = y;
        _pos_z = z;
    }

    this.showFire = function () {
        initFire();
        initEmbers();
        initHaze();
        this._isShow = true;
    }

    this.refresh = function () {
        _self.loop();
        _self.loop2();
        _self.loop3();
    }

    this.isShow = function () {
        return this._isShow;
    }

    this.hide = function () {
        _self.objs.map(obj => {
            _scene.remove(obj);
        });
        this._isShow = false;
    }

    this.show = function () {
        _self.objs.map(obj => {
            _scene.add(obj);
        });
        this._isShow = true;
    }

    //=====// Fire //========================================//     

    function initFire() {
        var _geometry, _shader, _mesh, _group;
        var _num = 50;

        var _x = new THREE.Vector3(1, 0, 0);
        var _y = new THREE.Vector3(0, 1, 0);
        var _z = new THREE.Vector3(0, 0, 1);

        var _tipTarget = new THREE.Vector3();
        var _tip = new THREE.Vector3();
        var _diff = new THREE.Vector3();

        var _quat = new THREE.Quaternion();
        var _quat2 = new THREE.Quaternion();

        (function () {
            initGeometry();
            initInstances();
            initShader();
            initMesh();
        })();

        function initGeometry() {
            _geometry = new THREE.InstancedBufferGeometry();
            _geometry.maxInstancedCount = _num;

            var shape = new THREE.PlaneBufferGeometry(500, 500);
            shape.translate(0, 0.4, 0);
            var data = shape.attributes;

            _geometry.addAttribute('position', new THREE.BufferAttribute(new Float32Array(data.position.array), 3));
            _geometry.addAttribute('uv', new THREE.BufferAttribute(new Float32Array(data.uv.array), 2));
            _geometry.addAttribute('normal', new THREE.BufferAttribute(new Float32Array(data.normal.array), 3));
            _geometry.setIndex(new THREE.BufferAttribute(new Uint16Array(shape.index.array), 1));
            shape.dispose();
        }

        function initInstances() {
            var orientation = new THREE.InstancedBufferAttribute(new Float32Array(_num * 4), 4);
            var randoms = new THREE.InstancedBufferAttribute(new Float32Array(_num), 1);
            var scale = new THREE.InstancedBufferAttribute(new Float32Array(_num * 2), 2);
            var life = new THREE.InstancedBufferAttribute(new Float32Array(_num), 1);

            for (let i = 0; i < _num; i++) {
                orientation.setXYZW(i, 0, 0, 0, 1);
                life.setX(i, i / _num + 1);
            }

            _geometry.addAttribute('orientation', orientation);
            _geometry.addAttribute('scale', scale);
            _geometry.addAttribute('life', life);
            _geometry.addAttribute('random', randoms);
        }

        function initShader() {
            var uniforms = {
                uMap: {
                    type: 't',
                    value: null
                },
                uColor1: {
                    type: 'c',
                    value: new THREE.Color(0x961800)
                }, // red
                uColor2: {
                    type: 'c',
                    value: new THREE.Color(0x4b5828)
                }, // yellow
                uTime: {
                    type: 'f',
                    value: 0
                },
            };

            _shader = new THREE.ShaderMaterial({
                uniforms: uniforms,
                vertexShader: fireVertexShader,
                fragmentShader: fireFragmentShader,
                blending: THREE.AdditiveBlending,
                transparent: true,
                depthTest: false,
                side: THREE.DoubleSide,
            });

            var textureLoader = new THREE.TextureLoader();
            textureLoader.load('images/myFire3/flame.png', t => _shader.uniforms.uMap.value = t);
        }

        function initMesh() {
            _group = new THREE.Group();
            _mesh = new THREE.Mesh(_geometry, _shader);
            _mesh.frustumCulled = false;
            _group.add(_mesh);
            _scene.add(_group);
            _self.objs.push(_group);
            _fire = _group;
        }

        _self.loop = function () {
            let e = 100;
            _shader.uniforms.uTime.value = e * 0.001;

            var life = _geometry.attributes.life;
            var orientation = _geometry.attributes.orientation;
            var scale = _geometry.attributes.scale;
            var randoms = _geometry.attributes.random;

            for (let i = 0; i < _num; i++) {
                var value = life.array[i];
                value += 0.04;

                if (value > 1) {
                    value -= 1;

                    _quat.setFromAxisAngle(_y, random(0, 3.14, 3));
                    _quat2.setFromAxisAngle(_x, random(-1, 1, 2) * 0.1);
                    _quat.multiply(_quat2);
                    _quat2.setFromAxisAngle(_z, random(-1, 1, 2) * 0.3);
                    _quat.multiply(_quat2);
                    orientation.setXYZW(i, _quat.x, _quat.y, _quat.z, _quat.w);

                    scale.setXY(i, random(0.8, 1.2, 3), random(0.8, 1.2, 3));
                    randoms.setX(i, random(0, 1, 3));
                }

                life.setX(i, value);
            }
            life.needsUpdate = true;
            orientation.needsUpdate = true;
            scale.needsUpdate = true;
            randoms.needsUpdate = true;

            _group.position.x = _pos_x; //Math.sin(e * 0.002) * 1.4;
            _group.position.y = _pos_y + 150; //Math.cos(e * 0.0014) * 0.2;
            _group.position.z = _pos_z; //Math.cos(e * 0.0014) * 0.5;

            let tipOffset = 0.4;
            _tipTarget.copy(_group.position);
            _tipTarget.y += tipOffset;
            _tip.lerp(_tipTarget, 0.1);

            _diff.copy(_tip);
            _diff.sub(_group.position);
            let length = _diff.length();
            //_group.scale.y = (length / tipOffset - 1) * 0.4 + 1;

            _group.quaternion.setFromUnitVectors(_y, _diff.normalize());
        }
    }

    //=====// Embers //========================================//     

    function initEmbers() {
        var _geometry, _shader, _points;
        var _num = 8;

        (function () {
            initGeometry();
            initShader();
            initMesh();
        })();

        function initGeometry() {
            _geometry = new THREE.BufferGeometry();
            _geometry.addAttribute('position', new THREE.BufferAttribute(new Float32Array(_num * 3), 3));
            _geometry.addAttribute('offset', new THREE.BufferAttribute(new Float32Array(_num * 3), 3));
            _geometry.addAttribute('size', new THREE.BufferAttribute(new Float32Array(_num), 1));
            _geometry.addAttribute('life', new THREE.BufferAttribute(new Float32Array(_num), 1));
            var scale = new THREE.InstancedBufferAttribute(new Float32Array(_num * 2), 2);
            _geometry.addAttribute('scale', scale);

            for (var i = 0; i < _num; i++) {
                _geometry.attributes.life.setX(i, random(0, 1, 3) + 1);
            }
        }

        function initShader() {
            var uniforms = {
                uMap: {
                    type: 't',
                    value: null
                },
                uColor: {
                    type: 'c',
                    value: new THREE.Color(0xffe61e)
                },
            };

            _shader = new THREE.ShaderMaterial({
                uniforms: uniforms,
                vertexShader: embersVertexShader,
                fragmentShader: embersFragmentShader,
                blending: THREE.AdditiveBlending,
                transparent: true,
                depthTest: false,
            });

            var textureLoader = new THREE.TextureLoader();
            textureLoader.load('images/myFire3/ember.png', t => _shader.uniforms.uMap.value = t);
        }

        function initMesh() {
            _points = new THREE.Points(_geometry, _shader);
            _points.frustumCulled = false;
            _scene.add(_points);
            _self.objs.push(_points);
        }

        _self.loop2 = function () {
            var life = _geometry.attributes.life;
            var position = _geometry.attributes.position;
            var size = _geometry.attributes.size;
            var offset = _geometry.attributes.offset;
            var scale = _geometry.attributes.scale;
            for (let i = 0; i < _num; i++) {
                var value = life.array[i];
                value += 0.02;

                if (value > 1) {
                    value -= 1;

                    position.setXYZ(i, _pos_x, _pos_y + 0.1, _pos_z);
                    offset.setXYZ(i,
                        random(-200, 200, 3),
                        random(200, 600, 3),
                        random(-100, 100, 3)
                    );
                    size.setX(i, random(20, 200, 3));

                    scale.setXY(i, 50, 50);
                }

                life.setX(i, value);
            }

            life.needsUpdate = true;
            position.needsUpdate = true;
            size.needsUpdate = true;
            offset.needsUpdate = true;
        }
    }

    //=====// Haze //========================================//     

    function initHaze() {
        var _geometry, _shader, _mesh;

        var _num = 4;

        var _z = new THREE.Vector3(0, 0, 1);
        var _quat = new THREE.Quaternion();
        var _quat2 = new THREE.Quaternion();

        (function () {
            initGeometry();
            initInstances();
            initShader();
            initMesh();
            window.addEventListener('resize', resizeHaze, false);
            resizeHaze();
        })();

        function initGeometry() {
            _geometry = new THREE.InstancedBufferGeometry();
            _geometry.maxInstancedCount = _num;

            var shape = new THREE.PlaneBufferGeometry(0.1, 0.1);
            var data = shape.attributes;

            _geometry.addAttribute('position', new THREE.BufferAttribute(new Float32Array(data.position.array), 3));
            _geometry.addAttribute('uv', new THREE.BufferAttribute(new Float32Array(data.uv.array), 2));
            _geometry.addAttribute('normal', new THREE.BufferAttribute(new Float32Array(data.normal.array), 3));
            _geometry.setIndex(new THREE.BufferAttribute(new Uint16Array(shape.index.array), 1));
            shape.dispose();
        }

        function initInstances() {
            var base = new THREE.InstancedBufferAttribute(new Float32Array(_num * 3), 3);
            var offset = new THREE.InstancedBufferAttribute(new Float32Array(_num * 3), 3);
            var orientation = new THREE.InstancedBufferAttribute(new Float32Array(_num * 4), 4);
            var scale = new THREE.InstancedBufferAttribute(new Float32Array(_num * 2), 2);
            var rotation = new THREE.InstancedBufferAttribute(new Float32Array(_num), 1);
            var life = new THREE.InstancedBufferAttribute(new Float32Array(_num), 1);

            for (let i = 0; i < _num; i++) {
                orientation.setXYZW(i, 0, 0, 0, 1);
                life.setX(i, i / _num + 1);
            }

            _geometry.addAttribute('base', base);
            _geometry.addAttribute('offset', offset);
            _geometry.addAttribute('orientation', orientation);
            _geometry.addAttribute('scale', scale);
            _geometry.addAttribute('rotation', rotation);
            _geometry.addAttribute('life', life);
        }

        function initShader() {
            let dpr = _renderer.getPixelRatio();
            var uniforms = {
                uMap: {
                    type: 't',
                    value: null
                },
                uMask: {
                    type: 't',
                    value: null
                },
                uResolution: {
                    type: 'v2',
                    value: new THREE.Vector2(_width * dpr, _height * dpr)
                },
            };

            _shader = new THREE.ShaderMaterial({
                uniforms: uniforms,
                vertexShader: hazeVertexShader,
                fragmentShader: hazeFragmentShader,
                transparent: true,
                depthTest: false,
            });

            var textureLoader = new THREE.TextureLoader();
            textureLoader.load('images/myFire3/haze.png', t => _shader.uniforms.uMask.value = t);
        }

        function initMesh() {
            _mesh = new THREE.Mesh(_geometry, _shader);
            _mesh.frustumCulled = false;
            _scene.add(_mesh);
            _self.objs.push(_mesh);
        }

        function resizeHaze() {
            let dpr = _renderer.getPixelRatio();
            _shader.uniforms.uMap.value = _rtt.texture;
            _shader.uniforms.uResolution.value.set(_width * dpr, _height * dpr);
        }

        _self.loop3 = function () {

            let e = 100;
            _mesh.visible = false;
            //_renderer.render(_scene, _camera, _rtt);
            _mesh.visible = true;

            var life = _geometry.attributes.life;
            var base = _geometry.attributes.base;
            var offset = _geometry.attributes.offset;
            var scale = _geometry.attributes.scale;
            var orientation = _geometry.attributes.orientation;
            var rotation = _geometry.attributes.rotation;
            for (let i = 0; i < _num; i++) {
                var value = life.array[i];
                value += 0.008;

                if (value > 1) {
                    value -= 1;

                    rotation.setX(i, random(0, 3.14, 3));

                    base.setXYZ(i, _pos_x, _pos_y + 0.1, _pos_z);
                    offset.setXYZ(i,
                        random(-220, 220, 3),
                        random(300, 600, 3),
                        0
                    );
                    //scale.setXY(i, random(0.6, 1.2, 3), random(0.6, 1.2, 3));
                    scale.setXY(i, 50, 50);
                }

                _quat.copy(_camera.quaternion);
                _quat2.setFromAxisAngle(_z, rotation.array[i]);
                _quat.multiply(_quat2);
                orientation.setXYZW(i, _quat.x, _quat.y, _quat.z, _quat.w);

                life.setX(i, value);
            }

            life.needsUpdate = true;
            base.needsUpdate = true;
            scale.needsUpdate = true;
            offset.needsUpdate = true;
            orientation.needsUpdate = true;
        }
    }


}

MyFire3.prototype.constructor = MyFire3;

export { MyFire3 }
View Code

如何使用(部分代码):

import { MyFire3 } from '../js.my/MyFire3.js';


let myFire3;


if (!myFire3) {
    myFire3 = new MyFire3();
    myFire3.config(scene, renderer, camera, controls);
    myFire3.setPosition(417, 0, 1134);
    myFire3.showFire();
} else {
    if (myFire3.isShow()) {
        myFire3.hide();
    } else {
        myFire3.show();
    }
}


function renderLoop() {
    requestAnimationFrame(renderLoop);

    myFire3 && myFire3.refresh && myFire3.refresh();
}

renderLoop();
View Code

说明:myFire3.config的参数中的controls未用到,scene、renderer、camera分别是three.js的Scene、WebGLRenderer和PerspectiveCamera对象

 

标签:life,geometry,float,THREE,three,火焰,var,new,js
来源: https://www.cnblogs.com/s0611163/p/15505772.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有