# webgl实现火焰效果

### 第一步：构建基础

```float noise(vec3 p){
vec3 i = floor(p);
vec4 a = dot(i, vec3(1., 57., 21.)) + vec4(0., 57., 21., 78.);
vec3 f = cos((p-i)*acos(-1.))*(-.5)+.5;
a = mix(sin(cos(a)*a),sin(cos(1.+a)*(1.+a)), f.x);
a.xy = mix(a.xz, a.yw, f.y);
return mix(a.x, a.y, f.z);
}```

### 第三步：修改片元着色器

```void main() {
vec2 v = -1.5 + 3. * v_TexCoord;```
```    vec3 org = vec3(0., -2., 4.);
vec3 dir = normalize(vec3(v.x*1.6, -v.y, -1.5));```
```    vec4 p = raymarch(org, dir);
float glow = p.w;```
```    vec4 col = mix(vec4(1.,.5,.1,1.), vec4(0.1,.5,1.,1.), p.y*.02+.4);```
```    gl_FragColor = mix(vec4(0.), col, pow(glow*2.,4.));
}```

v_TexCoord是我们传入的纹理坐标值，对其进行了乘与加处理，还记得第一步中说过的火焰位置吗？这两个参数就是用来调节的。

```float sphere(vec3 p, vec4 spr){
return length(spr.xyz-p) - spr.w;
}```
```float flame(vec3 p){
float d = sphere(p*vec3(1.,.5,1.), vec4(.0,-1.,.0,1.));
return d + (noise(p+vec3(.0,time*2.,.0)) + noise(p*3.)*.5)*.25*(p.y) ;
}```
```float scene(vec3 p){
return min(100.-length(p) , abs(flame(p)) );
}```
```vec4 raymarch(vec3 org, vec3 dir){
float d = 0.0, glow = 0.0, eps = 0.02;
vec3  p = org;
bool glowed = false;```
```    for(int i=0; i<64; i++)    {
d = scene(p) + eps;
p += d * dir;
if( d>eps )        {
if(flame(p) < .0)
glowed=true;
if(glowed)
glow = float(i)/64.;
}
}
return vec4(p,glow);
}```

raymarch函数的主要作用就是配合noise勾勒出火焰整体的外观，包括大小，颜色值，以及效果的位置等等。

### 全部的代码

```<template>
<div>
<canvas id="glcanvas" ref="webgl" width="375" height="667"></canvas>
</div>
</template>

<script>
/* eslint-disable */
import testImg from './static/img/img1.jpeg'
export default {
props: {
msg: String
},
mounted() {
attribute vec4 a_Position;
attribute vec2 a_TexCoord;
varying vec2 v_TexCoord;

void main() {
gl_Position = a_Position;
v_TexCoord = a_TexCoord;
}`
precision mediump float;
uniform sampler2D u_Sampler;
uniform float time;
varying vec2 v_TexCoord;

float noise(vec3 p){
vec3 i = floor(p);
vec4 a = dot(i, vec3(1., 57., 21.)) + vec4(0., 57., 21., 78.);
vec3 f = cos((p-i)*acos(-1.))*(-.5)+.5;
a = mix(sin(cos(a)*a),sin(cos(1.+a)*(1.+a)), f.x);
a.xy = mix(a.xz, a.yw, f.y);
return mix(a.x, a.y, f.z);
}

float sphere(vec3 p, vec4 spr){
return length(spr.xyz-p) - spr.w;
}

float flame(vec3 p){
float d = sphere(p*vec3(1.,.5,1.), vec4(.0,-1.,.0,1.));
return d + (noise(p+vec3(.0,time*2.,.0)) + noise(p*3.)*.5)*.25*(p.y) ;
}

float scene(vec3 p){
return min(100.-length(p) , abs(flame(p)) );
}

vec4 raymarch(vec3 org, vec3 dir){
float d = 0.0, glow = 0.0, eps = 0.02;
vec3 p = org;
bool glowed = false;

for(int i=0; i<64; i++){
d = scene(p) + eps;
p += d * dir;
if( d>eps ){
if(flame(p) < .0)
glowed=true;
if(glowed)
glow = float(i)/64.;
}
}
return vec4(p,glow);
}

void main() {
vec2 v = -1.5 + 3. * v_TexCoord;

vec3 org = vec3(0., -2., 4.);
vec3 dir = normalize(vec3(v.x*1.6, -v.y, -1.5));

vec4 p = raymarch(org, dir);
float glow = p.w;

vec4 col = mix(vec4(1.,.5,.1,1.), vec4(0.1,.5,1.,1.), p.y*.02+.4);

gl_FragColor = mix(vec4(0.), col, pow(glow*2.,4.));
}`

let canvas = this.\$refs.webgl

let gl = getWebGLContext(canvas);

let n = this.initVertexBuffers(gl);

this.inirTextures(gl, n);

let u_time = gl.getUniformLocation(gl.program, "time");

let newTime = 0.1;
let draw = function(){
newTime = newTime + 0.05;
gl.uniform1f(u_time, newTime);
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
requestAnimationFrame(draw);
}

draw()

},
methods: {
initVertexBuffers(gl){
var verticesTexCoords = new Float32Array([
-1.0, 1.0, 0.0, 1.0,
-1.0, -1.0, 0.0, 0.0,
1.0, 1.0, 1.0, 1.0,
1.0, -1.0, 1.0, 0.0,
]);
var n = 4;
var vertexCoordBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertexCoordBuffer);
gl.bufferData(gl.ARRAY_BUFFER, verticesTexCoords, gl.STATIC_DRAW);

var FSIZE = verticesTexCoords.BYTES_PER_ELEMENT;

var a_Position = gl.getAttribLocation(gl.program, 'a_Position');
gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, FSIZE * 4, 0);
gl.enableVertexAttribArray(a_Position);

var a_TexCoord = gl.getAttribLocation(gl.program, 'a_TexCoord');
gl.vertexAttribPointer(a_TexCoord, 2, gl.FLOAT, false, FSIZE * 4, FSIZE * 2);
gl.enableVertexAttribArray(a_TexCoord)
return n;
},
inirTextures(gl, n){
var texture = gl.createTexture();
var u_Sampler = gl.getUniformLocation(gl.program, 'u_Sampler');
var image = new Image();
image.crossOrigin = "anonymous";
image.src = testImg
return true;
},
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1);
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
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.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
gl.uniform1i(u_Sampler, 0);
gl.drawArrays(gl.TRIANGLE_STRIP, 0, n);
}
}
}
</script>

<style lang="scss">
#glcanvas{
background-color: #000;
}
</style>```