当前位置: 首页 > ds >正文

Shadertoy着色器移植到Three.js经验总结

Shadertoy是一个流行的在线平台,用于创建和分享WebGL片段着色器。里面有很多令人惊叹的画面,甚至3D场景。本人也移植了几个ShaderToy上的着色器。本文将详细介绍移植过程中需要注意的关键点。

1. 基本结构差异

想要移植ShaderToy的shader到three.js,需要先了解他们直接的差异。他们之间的差异主要在以下几个方面:

  1. ShaderToy没有顶点着色器
    ShaderTory上面的shader只有片元着色器,没有几何,移植到three.js上需要构造一个默认的顶点着色器,同时提供几何信息。这个也比较简单,使用两个三角形铺面整个视口即可。其实用其他的方式也可以,例如用一个大的三角形,只要有个物体几何能充满整个视口即可。

  2. ShaderToy提供了很多内置变量
    ShaderToy提供了不少内置变量,,在three.js中需要咱们自己用uniform来传入。ShaderToy内置变量处理方法如下:

全局变量含义three.js修改方案核心代码
uniform vec3 iResolution;视口大小,单位像素uniforms.iResolution = { value: new THREE.Vector2(options.width, options.hieght) }
uniform float iTime;从开始运行到现在的时间,单位秒var clock = new THREE.Clock();
this.uniforms.iTime.value = this.clock.getElapsedTime();
uniform float iTimeDelta;上一帧渲染到现在的时间,单位秒var clock = new THREE.Clock();
this.uniforms.iTimeDelta.value = this.clock.getDelta();
uniform int iFrame;第几帧this.uniforms.iFrame.value = this.uniforms.iFrame.value + 1;
uniform float iChannelTime[4];//
uniform vec3 iChannelResolution[4];每个通道的分辨率,单位像素uniforms[“iChannelResolution”] = { type: “v3v”, value: [ new THREE.Vector3(), new THREE.Vector3(), new THREE.Vector3(), new THREE.Vector3(), ] };
uniform vec4 iMouse;是vec4类型,xy是鼠标当前位置与单击位置,zw是单击位置document.addEventListener(‘mousemove’, (e) => { material.uniforms.iMouse.value.x = e.clientX; material.uniforms.iMouse.value.y = window.innerHeight - e.clientY; }); document.addEventListener(‘mousedown’, (e) => { material.uniforms.iMouse.value.z = e.clientX; material.uniforms.iMouse.value.w = window.innerHeight - e.clientY; });
uniform samplerXX iChanneli;通道的纹理uniforms[“iChannel” + i] = { type: “t”, value: texture }
  1. ShaderToy语法的差异

ShaderToy语法核心都是 GLSL,只是在在变量命名、输入输出方式、内置函数/变量等方面存在区别。以下是关键语法差异的对比:

ShaderToythree.js
入口函数是:mainImage(out vec4 fragColor, in vec2 fragCoord)入口函数是:void main()
fragCoord当前像素的坐标用的是gl_FragCoord
用变量fragColor作为输出通过 gl_FragColor 变量输出
不需要定义变量精度需定义精度:precision highp float; precision highp int;precision highp sampler2D;precision highp samplerCube;

移植基本模版

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Shadertoy to Three.js</title><style>body {margin: 0;overflow: hidden;}canvas {display: block;}</style>
</head><body><script src="./three.min.js"></script><script>const scene = new THREE.Scene();const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);const renderer = new THREE.WebGLRenderer({ antialias: true });renderer.setSize(window.innerWidth, window.innerHeight);document.body.appendChild(renderer.domElement);console.log(renderer.capabilities.isWebGL2)const geometry = new THREE.BufferGeometry();geometry.setAttribute("position", new THREE.Float32BufferAttribute([-1, -1, 0,1, -1, 0,1, 1, 0,-1, 1, 0], 3))geometry.setIndex([0, 1, 2, 0, 2, 3]);const material = new THREE.RawShaderMaterial({uniforms: {iResolution: { value: new THREE.Vector2(window.innerWidth, window.innerHeight) },iTime: { value: 0 },mouse: { value: new THREE.Vector4(0, 0, 0, 0) },},// glslVersion: THREE.GLSL3,vertexShader: `#version 300 esin vec3 position;void main() {gl_Position = vec4(position, 1.0);}`,fragmentShader: `#version 300 es
precision highp float;
precision highp int;
precision highp sampler2D;
precision highp samplerCube;
out vec4 fragColor;uniform vec2 iResolution;
uniform float iTime;void main(  )
{vec2 uv = gl_FragCoord.xy/iResolution.xy;// [0,1]uv -= 0.5;uv.x *= iResolution.x / iResolution.y;float d = length(uv);float r = 0.3;float c = smoothstep(r, r + 0.002, d);fragColor = vec4(vec3(c), 1.);
}
`});const plane = new THREE.Mesh(geometry, material);scene.add(plane);window.addEventListener('resize', () => {camera.aspect = window.innerWidth / window.innerHeight;camera.updateProjectionMatrix();renderer.setSize(window.innerWidth, window.innerHeight);material.uniforms.iResolution.value.set(window.innerWidth, window.innerHeight);});document.addEventListener('mousemove', (e) => {material.uniforms.iMouse.value.x = e.clientX;material.uniforms.iMouse.value.y = window.innerHeight - e.clientY;});document.addEventListener('mousedown', (e) => {material.uniforms.iMouse.value.z = e.clientX;material.uniforms.iMouse.value.w = window.innerHeight - e.clientY;});function animate() {requestAnimationFrame(animate);material.uniforms.iTime.value = performance.now() / 1000; // 秒为单位renderer.render(scene, camera);}animate();</script>
</body></html>

这个模版是画圆的实例,效果如下:

有以下几点需要注意:

  1. 两个三角形面片的坐标已经在[-1, 1]之间,不需要在VertexShader进行坐标变换,所有顶点着色器代码没有MVP矩阵:
#version 300 es
in vec3 position;
void main() {gl_Position = vec4(position, 1.0);
}
  1. 使用了RawShaderMaterial而不是ShaderMaterial,这样可对Shader代码进行完全掌控
  2. 语法上使用了WebGL2.0的语法,如果使用1.0的语法,需去掉版本声明、一些关键字

最后展示一个稍微复杂点的Shader效果:

结语

将Shadertoy着色器移植到Three.js需要对两者之间的差异有清晰理解。正确处理全局变量、语法、渲染管线等关方面的差异,可将绝大多数Shadertoy效果成功移植到Three.js项目中。

http://www.xdnf.cn/news/2564.html

相关文章:

  • 【Linux系统】详解Linux权限
  • AI工作流自动化与智能应用开发平台
  • WEB服务器的部署及优化
  • 线上JVM调优与全栈性能优化 - Java架构师面试实战
  • DataStreamAPI实践原理——快速上手
  • 学习笔记—双指针算法—移动零
  • [原创](现代Delphi 12指南):[macOS 64bit App开发]: NSString类型与CFStringRef类型字符串相互转换.
  • 通过数据增强打造抗噪音多模态大模型
  • MySQL 大数据量分页查询优化指南
  • Git 撤回合并提交
  • WPF之XAML基础
  • AlexNet网络搭建
  • OneNet云平台
  • java16
  • Java快速上手之实验五
  • 若依脱敏功能升级:接口返回想脱就脱,想不脱就不脱(实现灵活可控制的数据脱敏)
  • 手撕——贪吃蛇小游戏(下)
  • 【quantity】1 创建 crates.io 账号并上传 Rust 库
  • 数据库查询艺术:从单表操作到多表联查的全面指南
  • Rollup、Webpack、Esbuild 和 Vite 前端打包工具
  • Redis01-基础-入门
  • 华为仓颉编程语言的实际用法与使用领域详解
  • OpenCV实验室工具的使用
  • 【银河麒麟高级服务器操作系统】在VMware虚拟机情况下出现软锁处理过程
  • C/C++死锁和活锁
  • k8s学习记录(五):Pod亲和性详解
  • 解决两个技术问题后小有感触-QZ Tray使用经验小总结
  • 分布式GPU上计算长向量模的方法
  • 数据一致性问题剖析与实践(四)——竞态条件竞争导致的一致性问题
  • 制作一款打飞机游戏26:精灵编辑器