You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

新手求教:如何在Three.js ShaderMaterial中实现ShaderToy?

ShaderToy代码转Three.js ShaderMaterial 教程

1. 基础转换步骤

1.1 搭建Three.js基础场景

先创建最基本的Three.js运行环境,用来承载Shader效果:

import * as THREE from 'three';

// 初始化核心组件
const scene = new THREE.Scene();
const camera = new THREE.OrthographicCamera(-1, 1, 1, -1, 0, 1);
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

// 创建全屏平面(Shader会渲染在这个平面上)
const plane = new THREE.PlaneBufferGeometry(2, 2);

1.2 编写顶点着色器

顶点着色器只需传递UV坐标给片元着色器,逻辑非常简单:

varying vec2 vUv;

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

1.3 转换片元着色器

将ShaderToy代码适配Three.js规范,按以下步骤修改:

  1. 开头添加精度声明,避免渲染异常
  2. 定义对应ShaderToy输入的uniform变量
  3. 复制原ShaderToy中所有自定义函数
  4. 修改mainImage里的变量引用,替换为uniform变量
  5. 添加main函数作为Three.js的片元入口

示例结构如下:

precision highp float;

// 对应ShaderToy的输入变量,自定义参数也在这里声明
uniform float time;          // 替换ShaderToy的iTime
uniform vec2 resolution;     // 替换ShaderToy的iResolution
uniform sampler2D envMap;    // 替换ShaderToy的iChannel2
uniform float bounceTime;    // 自定义动态参数:反弹时间

// 复制原ShaderToy中的所有自定义函数(比如hash3、map、norm等)
vec3 hash3(vec2 p) {
    vec3 q = vec3(dot(p,vec2(127.1,311.7)), dot(p,vec2(269.5,183.3)), dot(p,vec2(419.2,371.9)));
    return fract(sin(q)*43758.5453);
}

// ... 粘贴原ShaderToy的其他函数 ...

// 保留原ShaderToy的mainImage函数,替换变量引用
void mainImage( out vec4 fragColor, in vec2 fragCoord ) {
    vec2 p = (2.0*fragCoord.xy-resolution.xy)/max(resolution.x,resolution.y);
    
    // 把原代码中固定的反弹时间换成uniform变量
    float bounce = bounceTime;
    
    // 把原代码中的texture(iChannel2, ...)替换为texture(envMap, ...)
    // ... 其他原ShaderToy逻辑 ...
}

// Three.js片元着色器入口函数
void main() {
    vec4 fragColor;
    mainImage(fragColor, gl_FragCoord.xy);
    gl_FragColor = fragColor;
}

1.4 创建ShaderMaterial并挂载

定义uniform对象,创建材质并绑定到平面:

// 初始化uniform变量
const uniforms = {
    time: { value: 0 },
    resolution: { value: new THREE.Vector2(window.innerWidth, window.innerHeight) },
    envMap: { 
        value: new THREE.TextureLoader().load('你的环境贴图路径') 
    },
    bounceTime: { value: 1.5 } // 默认反弹时间
};

// 修正纹理上下颠倒问题
uniforms.envMap.value.flipY = false;

// 创建ShaderMaterial
const shaderMaterial = new THREE.ShaderMaterial({
    uniforms: uniforms,
    vertexShader: document.getElementById('vertexShader').textContent,
    fragmentShader: document.getElementById('fragmentShader').textContent
});

// 将材质绑定到平面并添加到场景
const mesh = new THREE.Mesh(plane, shaderMaterial);
scene.add(mesh);

2. 动态化参数实现

2.1 实时更新时间

在动画循环中更新time变量,模拟ShaderToy的时间流逝:

function animate() {
    requestAnimationFrame(animate);
    // 用时间戳计算更准确的流逝时间
    uniforms.time.value = performance.now() / 1000;
    renderer.render(scene, camera);
}
animate();

2.2 动态调整反弹时间

直接修改bounceTime的value即可,可配合GUI工具可视化调整:

// 示例:用dat.GUI做控制面板(需提前引入dat.GUI)
import * as dat from 'dat.gui';
const gui = new dat.GUI();
gui.add(uniforms.bounceTime, 'value', 0.5, 5.0)
   .name('反弹时间')
   .onChange(() => {
       // 参数修改后自动生效,无需额外操作
   });

2.3 动态更换环境贴图

更换纹理时必须设置needsUpdate通知Three.js更新:

// 示例:点击按钮切换贴图
document.getElementById('changeEnvBtn').addEventListener('click', () => {
    const loader = new THREE.TextureLoader();
    loader.load('新的环境贴图路径', (newTexture) => {
        newTexture.flipY = false;
        uniforms.envMap.value = newTexture;
        uniforms.envMap.value.needsUpdate = true;
    });
});

2.4 适配窗口大小变化

监听窗口resize事件,更新分辨率uniform:

window.addEventListener('resize', () => {
    uniforms.resolution.value.set(window.innerWidth, window.innerHeight);
    renderer.setSize(window.innerWidth, window.innerHeight);
});

内容的提问来源于stack exchange,提问作者Pallab

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 07:32:07