预览效果
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();