three.js shader 火焰效果

预览效果

在这里插入图片描述

1、懒人直接上代码,全部代码效果


import * as THREE from 'https://esm.sh/three';
import { OrbitControls } from 'https://esm.sh/three/examples/jsm/controls/OrbitControls';


let scene: THREE.Scene, renderer: THREE.WebGLRenderer, camera: THREE.PerspectiveCamera,
    controls: OrbitControls, material: THREE.Material,
    sizes = { width: window.innerWidth, height: window.innerHeight }
const clock = new THREE.Clock();

function init() {
  let w = window.innerWidth;
  let h = window.innerHeight;

  scene = new THREE.Scene();
  scene.background = new THREE.Color('#000000');

  camera = new THREE.PerspectiveCamera(75, w / h, 0.1, 100000);
  camera.position.set(0, 0, 2);
  camera.lookAt(new THREE.Vector3());

  renderer = new THREE.WebGLRenderer({
    antialias: true, alpha: true,
  });
  renderer.outputColorSpace = THREE.SRGBColorSpace
  renderer.shadowMap.enabled = true;
  renderer.shadowMap.type = THREE.BasicShadowMap;
  renderer.setPixelRatio(window.devicePixelRatio);
  renderer.setSize(w, h);

  const app = document.getElementById("app") as Element;
  while (app.firstChild) app.removeChild(app.firstChild)
  app?.appendChild(renderer.domElement);

  controls = new OrbitControls(camera, renderer.domElement);
  controls.enableDamping = true

  // 光照
  const ambientLight = new THREE.AmbientLight(0xffffff, 1);
  scene.add(ambientLight);
  const directionalLight = new THREE.DirectionalLight(0xffffff, 1.5);
  directionalLight.castShadow = true;
  directionalLight.shadow.camera.far = 20000;
  directionalLight.shadow.mapSize.set(2048, 2048);
  directionalLight.shadow.normalBias = 0.05;
  directionalLight.shadow.camera.top = 80;
  directionalLight.shadow.camera.bottom = -80;
  directionalLight.shadow.camera.left = -80;
  directionalLight.shadow.camera.right = 80;
  directionalLight.position.set(60, 120, 80);
  scene.add(directionalLight);

  window.addEventListener("resize", resize);
}

// 顶点着色器
const vertexShader = `
varying vec2 vUv;

void main() {
  vUv = uv;
  gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`;

// 片元着色器
const fragmentShader = `

uniform float uTime;
varying vec2 vUv;
// 噪声函数
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.)) * (-0.5) + 0.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, uTime * 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.0)
        glowed = true;
      if (glowed)
        glow = float(i) / 64.;
    }
  }
  return vec4(p, glow);
}


void main() {
  vec2 v = -1.0 + 2.0 * vUv;
  // v.x *= iResolution.x / iResolution.y;

  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 * 0.02 + 0.4);

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


function initShader() {
  const material = new THREE.ShaderMaterial({
    vertexShader: vertexShader,
    fragmentShader: fragmentShader,
    side: THREE.DoubleSide,
    transparent: true,
    uniforms: {
      uTime: {
        value: 0,
      },
      uMouse: {
        value: new THREE.Vector2(0, 0),
      },
      uResolution: {
        value: new THREE.Vector2(window.innerWidth, window.innerHeight),
      },
      uVelocity: {
        value: 3,
      },
      uColor1: {
        value: new THREE.Color('#ff801a'),
      },
      uColor2: {
        value: new THREE.Color('#ff5718'),
      },
    },
  });

  const geometry = new THREE.PlaneGeometry(2, 2, 1, 1);
  const mesh = new THREE.Mesh(geometry, material);
  scene.add(mesh);

  return material
}

init()
material = initShader()
render();

function render() {
  const time = clock.getElapsedTime();
  if (material) material.uniforms.uTime.value = time;
  controls.update()
  renderer.render(scene, camera);
  requestAnimationFrame(render);
}

function resize() {
  const w = window.innerWidth;
  const h = window.innerHeight;
  sizes.width = w;
  sizes.height = h;
  renderer.setSize(w, h);
  renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))
  camera.aspect = w / h;
  camera.updateProjectionMatrix();
}

2、第一步初始化基本的three.js 代码结构

1、导入模块初始化变量

import * as THREE from 'https://esm.sh/three';
import { OrbitControls } from 'https://esm.sh/three/examples/jsm/controls/OrbitControls';


let scene: THREE.Scene, renderer: THREE.WebGLRenderer, camera: THREE.PerspectiveCamera,
    controls: OrbitControls, material: THREE.Material,
    sizes = { width: window.innerWidth, height: window.innerHeight }
const clock = new THREE.Clock();

3、第二步初定义three.js基本的方法

function init() {
  let w = window.innerWidth;
  let h = window.innerHeight;

  scene = new THREE.Scene();
  scene.background = new THREE.Color('#000000');

  camera = new THREE.PerspectiveCamera(75, w / h, 0.1, 100000);
  camera.position.set(0, 0, 2);
  camera.lookAt(new THREE.Vector3());

  renderer = new THREE.WebGLRenderer({
    antialias: true, alpha: true,
  });
  renderer.outputColorSpace = THREE.SRGBColorSpace
  renderer.shadowMap.enabled = true;
  renderer.shadowMap.type = THREE.BasicShadowMap;
  renderer.setPixelRatio(window.devicePixelRatio);
  renderer.setSize(w, h);

  const app = document.getElementById("app") as Element;
  while (app.firstChild) app.removeChild(app.firstChild)
  app?.appendChild(renderer.domElement);

  controls = new OrbitControls(camera, renderer.domElement);
  controls.enableDamping = true

  // 光照
  const ambientLight = new THREE.AmbientLight(0xffffff, 1);
  scene.add(ambientLight);
  const directionalLight = new THREE.DirectionalLight(0xffffff, 1.5);
  directionalLight.castShadow = true;
  directionalLight.shadow.camera.far = 20000;
  directionalLight.shadow.mapSize.set(2048, 2048);
  directionalLight.shadow.normalBias = 0.05;
  directionalLight.shadow.camera.top = 80;
  directionalLight.shadow.camera.bottom = -80;
  directionalLight.shadow.camera.left = -80;
  directionalLight.shadow.camera.right = 80;
  directionalLight.position.set(60, 120, 80);
  scene.add(directionalLight);

  window.addEventListener("resize", resize);
}

function render() {
  const time = clock.getElapsedTime();
  if (material) material.uniforms.uTime.value = time;
  controls.update()
  renderer.render(scene, camera);
  requestAnimationFrame(render);
}

function resize() {
  const w = window.innerWidth;
  const h = window.innerHeight;
  sizes.width = w;
  sizes.height = h;
  renderer.setSize(w, h);
  renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))
  camera.aspect = w / h;
  camera.updateProjectionMatrix();
}

4、第三步编写shader


// 顶点着色器
const vertexShader = `
varying vec2 vUv;

void main() {
  vUv = uv;
  gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`;

// 片元着色器
const fragmentShader = `

uniform float uTime;
varying vec2 vUv;
// 噪声函数
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.)) * (-0.5) + 0.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, uTime * 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.0)
        glowed = true;
      if (glowed)
        glow = float(i) / 64.;
    }
  }
  return vec4(p, glow);
}


void main() {
  vec2 v = -1.0 + 2.0 * vUv;
  // v.x *= iResolution.x / iResolution.y;

  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 * 0.02 + 0.4);

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


function initShader() {
  const material = new THREE.ShaderMaterial({
    vertexShader: vertexShader,
    fragmentShader: fragmentShader,
    side: THREE.DoubleSide,
    transparent: true,
    uniforms: {
      uTime: {
        value: 0,
      },
      uMouse: {
        value: new THREE.Vector2(0, 0),
      },
      uResolution: {
        value: new THREE.Vector2(window.innerWidth, window.innerHeight),
      },
      uVelocity: {
        value: 3,
      },
      uColor1: {
        value: new THREE.Color('#ff801a'),
      },
      uColor2: {
        value: new THREE.Color('#ff5718'),
      },
    },
  });

  const geometry = new THREE.PlaneGeometry(2, 2, 1, 1);
  const mesh = new THREE.Mesh(geometry, material);
  scene.add(mesh);

  return material
}

5、第四步运行

init()
material = initShader()
render();
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

淋雨一直走~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值