我正在第一步编码。我在互联网上做了一些类(class),然后进行了Three.js实验,现在我想继续学习使用Shaders进行实验。
我找到了Shadertoy.com,这真是太好了!有许多不同的实验具有令人难以置信的效果。我试图在Three.js中使用这些着色器之一,但并不是那么容易。
着色器已经写好了,这是事实。但是我不知道该怎么办,我也不知道该如何使用它。
因为它不仅复制并粘贴代码。我必须编写一种关系,可以将其中一些惊人的效果应用于Three.js几何体。我必须使用制服,而且我不知道如何才能知道可以使用哪些制服,以及如何使用它们。
我开始在Shadertoy中看到教程,并在Internet上看到一些文章,看起来好像是非常抽象的东西。我认为在开始理解该语言之前,我应该学习很多数学。
您有什么建议可以开始吗?
也许比我想象的要简单,我可以复制,粘贴和试验HTML文档中的代码?
最佳答案
Shadertoy是一个相对复杂的程序。它可以将音频输入到着色器中,将视频输入到着色器中,从着色器生成音频数据,包括2d和立方体贴图在内的各种纹理。支持所有这些功能不是一件容易的事。
也就是说,可以很容易地使用基本着色器,请参见下面的示例。但是shadertoy着色器并不是真正设计为在three.js中用作网格上的 Material 。
如果您想了解WebGL的原因和工作方式,请参阅http://webglfundamentals.org
const vs = `
attribute vec4 position;
void main() {
gl_Position = position;
}
`;
const userShader = `
// FROM: https://www.shadertoy.com/view/4sdXDl
//spikey
#define SHAPE length(z.yz)
//normal
//#define SHAPE length(z.xyz)
//bizarro
//#define SHAPE length(z.yz-z.xx)
//etc...
#define HIGH_QUAL
#ifdef HIGH_QUAL
#define MARCH_STEPS 199
#else
#define MARCH_STEPS 99
#endif
float k=7.0+3.0*sin(iGlobalTime*0.15);
vec3 mcol=vec3(0.0);
void AbsBox(inout vec4 z){//abs box by kali
z.xyz=abs(z.xyz+1.0)-1.0;
z*=1.5/clamp(dot(z.xyz,z.xyz),0.25,1.0);
}
void Bulb(inout vec4 z, in vec4 c){//mandelBulb by twinbee
float r = length(z.xyz);
float zo = asin(z.z / r) * k + iGlobalTime*0.15;
float zi = atan(z.y, z.x) * 7.0;
z=pow(r, k-1.0)*vec4(r*vec3(cos(zo)*vec2(cos(zi),sin(zi)),sin(zo)),z.w*k)+c;
}
float DE(vec3 p){
vec4 c = vec4(p,1.0),z = c;
Bulb(z,c);
float r0=(length(z.xyz)-1.15)/z.w;
z.xyz-=1.0;
for(int i=0;i<7;i++)AbsBox(z);
float r=SHAPE;
mcol.rgb=vec3(1.0,0.5,0.2)+abs(sin(0.2*r+100.0*z.yxz/z.w));
return 0.5 * max((r-1.0) / z.w,-r0);
}
vec3 sky(vec3 rd, vec3 L){//modified bananaft's & public_int_i's code
float d=0.4*dot(rd,L)+0.6;
//return vec3(d);
rd.y+=sin(sqrt(clamp(-rd.y,0.0,0.9))*90.0)*0.45*max(-0.1,rd.y);
rd=abs(rd);
float y=max(0.,L.y),sun=max(1.-(1.+10.*y+rd.y)*length(rd-L),0.)
+.3*pow(1.-rd.y,12.)*(1.6-y);
return d*mix(vec3(0.3984,0.5117,0.7305),vec3(0.7031,0.4687,0.1055),sun)
*((.5+pow(y,.4))*(1.5-abs(L.y))+pow(sun,5.2)*y*(5.+15.0*y));
}
float rnd;
void randomize(in vec2 p){rnd=fract(float(iFrame)+sin(dot(p,vec2(13.3145,117.7391)))*42317.7654321);}
float ShadAO(in vec3 ro, in vec3 rd){
float t=0.0,s=1.0,d,mn=0.01;
for(int i=0;i<12;i++){
d=max(DE(ro+rd*t)*1.5,mn);
s=min(s,d/t+t*0.5);
t+=d;
}
return s;
}
vec3 scene(vec3 ro, vec3 rd){
vec3 L=normalize(vec3(0.4,0.025,0.5));
vec3 bcol=sky(rd,L);
vec4 col=vec4(0.0);//color accumulator
float t=DE(ro)*rnd,d,od=1.0,px=1.0/iResolution.x;
for(int i=0;i<MARCH_STEPS;i++){
d=DE(ro);
if(d<px*t){
float dif=clamp(1.0-d/od,0.2,1.0);
vec3 scol=mcol*dif*(1.3-0.3*t);
#ifdef HIGH_QUAL
vec2 s=vec2(DE(ro+d*4.0*L),DE(ro+d*16.0*L));
scol*=clamp(0.5*s.x/d+(s.y/d)/8.0,0.0,1.0);
#endif
float alpha=(1.0-col.w)*clamp(1.0-d/(px*t),0.0,1.0);
col+=vec4(clamp(scol,0.0,1.0),1.0)*alpha;
if(col.w>0.9)break;
}
t+=d;ro+=rd*d;od=d;
if(t>6.0)break;
}
col.rgb+=bcol*(1.0-clamp(col.w,0.0,1.0));
return col.rgb;
}
mat3 lookat(vec3 fw){
fw=normalize(fw);vec3 rt=normalize(cross(fw,vec3(0.0,1.0,0.0)));return mat3(rt,cross(rt,fw),fw);
}
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
randomize(fragCoord);
float tim=iGlobalTime*0.3,r=2.0+cos(tim*0.7);
vec2 uv=(fragCoord-0.5*iResolution.xy)/iResolution.x;
vec3 ro=vec3(sin(tim)*r,sin(tim*0.4),cos(tim)*r);
vec3 rd=lookat(-ro)*normalize(vec3(uv,1.0));
//rd+=2.0*cross(qrt.xyz,cross(qrt.xyz,rd)+qrt.w*rd);
fragColor=vec4(scene(ro,rd)*2.0,1.0);
}
`;
// FROM shadertoy.com
const shadertoyBoilerplate = `
#extension GL_OES_standard_derivatives : enable
//#extension GL_EXT_shader_texture_lod : enable
#ifdef GL_ES
precision highp float;
#endif
uniform vec3 iResolution;
uniform float iGlobalTime;
uniform float iChannelTime[4];
uniform vec4 iMouse;
uniform vec4 iDate;
uniform float iSampleRate;
uniform vec3 iChannelResolution[4];
uniform int iFrame;
uniform float iTimeDelta;
uniform float iFrameRate;
struct Channel
{
vec3 resolution;
float time;
};
uniform Channel iChannel[4];
uniform sampler2D iChannel0;
uniform sampler2D iChannel1;
uniform sampler2D iChannel2;
uniform sampler2D iChannel3;
void mainImage( out vec4 c, in vec2 f );
${userShader}
void main( void ){
vec4 color = vec4(0.0,0.0,0.0,1.0);
mainImage( color, gl_FragCoord.xy );
color.w = 1.0;
gl_FragColor = color;
}
`;
const $ = document.querySelector.bind(document);
const camera = new THREE.Camera();
camera.position.z = 1;
const scene = new THREE.Scene();
const geometry = new THREE.BufferGeometry();
const vertices = new Float32Array([
-1, -1,
1, -1,
-1, 1,
-1, 1,
1, -1,
1, 1,
]);
geometry.addAttribute( 'position', new THREE.BufferAttribute( vertices, 2 ) );
const uniforms = {
iGlobalTime: { type: "f", value: 1.0 },
iResolution: { type: "v3", value: new THREE.Vector3() },
};
const material = new THREE.RawShaderMaterial({
uniforms: uniforms,
vertexShader: vs,
fragmentShader: shadertoyBoilerplate,
});
var mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
var renderer = new THREE.WebGLRenderer();
document.body.appendChild(renderer.domElement);
resize(true);
render(0);
function resize(force) {
var canvas = renderer.domElement;
var dpr = 1; //window.devicePixelRatio; // make 1 or less if too slow
var width = canvas.clientWidth * dpr;
var height = canvas.clientHeight * dpr;
if (force || width != canvas.width || height != canvas.height) {
renderer.setSize( width, height, false );
uniforms.iResolution.value.x = renderer.domElement.width;
uniforms.iResolution.value.y = renderer.domElement.height;
}
}
function render(time) {
resize();
uniforms.iGlobalTime.value = time * 0.001;
renderer.render(scene, camera);
requestAnimationFrame(render);
}
canvas {
border: 1px solid black;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r74/three.min.js"></script>
shadertoy中的上述代码将
gl_FragCoord
作为输入传递给用户的着色器,该着色器是在 Canvas 中绘制的像素的像素坐标。对于模型,我们可以改为传递UV坐标,我们只需要选择一个分辨率来乘以它们,因为UV坐标通常从0到1,shadertoy着色器期望的canvas宽度为0, Canvas 的高度为0。
例:
const vs = `
varying vec2 vUv;
void main()
{
vUv = uv;
vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );
gl_Position = projectionMatrix * mvPosition;
}
`;
const userShader = `
// FROM: https://www.shadertoy.com/view/4sdXDl
//spikey
#define SHAPE length(z.yz)
//normal
//#define SHAPE length(z.xyz)
//bizarro
//#define SHAPE length(z.yz-z.xx)
//etc...
#define HIGH_QUAL
#ifdef HIGH_QUAL
#define MARCH_STEPS 199
#else
#define MARCH_STEPS 99
#endif
float k=7.0+3.0*sin(iGlobalTime*0.15);
vec3 mcol=vec3(0.0);
void AbsBox(inout vec4 z){//abs box by kali
z.xyz=abs(z.xyz+1.0)-1.0;
z*=1.5/clamp(dot(z.xyz,z.xyz),0.25,1.0);
}
void Bulb(inout vec4 z, in vec4 c){//mandelBulb by twinbee
float r = length(z.xyz);
float zo = asin(z.z / r) * k + iGlobalTime*0.15;
float zi = atan(z.y, z.x) * 7.0;
z=pow(r, k-1.0)*vec4(r*vec3(cos(zo)*vec2(cos(zi),sin(zi)),sin(zo)),z.w*k)+c;
}
float DE(vec3 p){
vec4 c = vec4(p,1.0),z = c;
Bulb(z,c);
float r0=(length(z.xyz)-1.15)/z.w;
z.xyz-=1.0;
for(int i=0;i<7;i++)AbsBox(z);
float r=SHAPE;
mcol.rgb=vec3(1.0,0.5,0.2)+abs(sin(0.2*r+100.0*z.yxz/z.w));
return 0.5 * max((r-1.0) / z.w,-r0);
}
vec3 sky(vec3 rd, vec3 L){//modified bananaft's & public_int_i's code
float d=0.4*dot(rd,L)+0.6;
//return vec3(d);
rd.y+=sin(sqrt(clamp(-rd.y,0.0,0.9))*90.0)*0.45*max(-0.1,rd.y);
rd=abs(rd);
float y=max(0.,L.y),sun=max(1.-(1.+10.*y+rd.y)*length(rd-L),0.)
+.3*pow(1.-rd.y,12.)*(1.6-y);
return d*mix(vec3(0.3984,0.5117,0.7305),vec3(0.7031,0.4687,0.1055),sun)
*((.5+pow(y,.4))*(1.5-abs(L.y))+pow(sun,5.2)*y*(5.+15.0*y));
}
float rnd;
void randomize(in vec2 p){rnd=fract(float(iFrame)+sin(dot(p,vec2(13.3145,117.7391)))*42317.7654321);}
float ShadAO(in vec3 ro, in vec3 rd){
float t=0.0,s=1.0,d,mn=0.01;
for(int i=0;i<12;i++){
d=max(DE(ro+rd*t)*1.5,mn);
s=min(s,d/t+t*0.5);
t+=d;
}
return s;
}
vec3 scene(vec3 ro, vec3 rd){
vec3 L=normalize(vec3(0.4,0.025,0.5));
vec3 bcol=sky(rd,L);
vec4 col=vec4(0.0);//color accumulator
float t=DE(ro)*rnd,d,od=1.0,px=1.0/iResolution.x;
for(int i=0;i<MARCH_STEPS;i++){
d=DE(ro);
if(d<px*t){
float dif=clamp(1.0-d/od,0.2,1.0);
vec3 scol=mcol*dif*(1.3-0.3*t);
#ifdef HIGH_QUAL
vec2 s=vec2(DE(ro+d*4.0*L),DE(ro+d*16.0*L));
scol*=clamp(0.5*s.x/d+(s.y/d)/8.0,0.0,1.0);
#endif
float alpha=(1.0-col.w)*clamp(1.0-d/(px*t),0.0,1.0);
col+=vec4(clamp(scol,0.0,1.0),1.0)*alpha;
if(col.w>0.9)break;
}
t+=d;ro+=rd*d;od=d;
if(t>6.0)break;
}
col.rgb+=bcol*(1.0-clamp(col.w,0.0,1.0));
return col.rgb;
}
mat3 lookat(vec3 fw){
fw=normalize(fw);vec3 rt=normalize(cross(fw,vec3(0.0,1.0,0.0)));return mat3(rt,cross(rt,fw),fw);
}
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
randomize(fragCoord);
float tim=iGlobalTime*0.3,r=2.0+cos(tim*0.7);
vec2 uv=(fragCoord-0.5*iResolution.xy)/iResolution.x;
vec3 ro=vec3(sin(tim)*r,sin(tim*0.4),cos(tim)*r);
vec3 rd=lookat(-ro)*normalize(vec3(uv,1.0));
//rd+=2.0*cross(qrt.xyz,cross(qrt.xyz,rd)+qrt.w*rd);
fragColor=vec4(scene(ro,rd)*2.0,1.0);
}
`;
// FROM shadertoy.com
const shadertoyBoilerplate = `
#extension GL_OES_standard_derivatives : enable
//#extension GL_EXT_shader_texture_lod : enable
#ifdef GL_ES
precision highp float;
#endif
uniform vec3 iResolution;
uniform float iGlobalTime;
uniform float iChannelTime[4];
uniform vec4 iMouse;
uniform vec4 iDate;
uniform float iSampleRate;
uniform vec3 iChannelResolution[4];
uniform int iFrame;
uniform float iTimeDelta;
uniform float iFrameRate;
struct Channel
{
vec3 resolution;
float time;
};
uniform Channel iChannel[4];
uniform sampler2D iChannel0;
uniform sampler2D iChannel1;
uniform sampler2D iChannel2;
uniform sampler2D iChannel3;
varying vec2 vUv;
void mainImage( out vec4 c, in vec2 f );
${userShader}
void main( void ){
vec4 color = vec4(0.0,0.0,0.0,1.0);
mainImage( color, vUv * iResolution.xy );
color.w = 1.0;
gl_FragColor = color;
}
`;
const $ = document.querySelector.bind(document);
const fieldOfView = 45;
const zNear = .1;
const zFar = 100;
const camera = new THREE.PerspectiveCamera(fieldOfView, 1, zNear, zFar);
camera.position.z = 3;
const scene = new THREE.Scene();
const geometry = new THREE.BoxGeometry(1, 1, 1);
const uniforms = {
iGlobalTime: { type: "f", value: 1.0 },
iResolution: { type: "v3", value: new THREE.Vector3() },
};
// choose a resolution to pass to the shader
uniforms.iResolution.value.x = 100;
uniforms.iResolution.value.y = 100;
const material = new THREE.ShaderMaterial({
uniforms: uniforms,
vertexShader: vs,
fragmentShader: shadertoyBoilerplate,
});
const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
var renderer = new THREE.WebGLRenderer();
document.body.appendChild(renderer.domElement);
resize(true);
render(0);
function resize(force) {
const canvas = renderer.domElement;
const dpr = 1; //window.devicePixelRatio; // make 1 or less if too slow
const width = canvas.clientWidth * dpr;
const height = canvas.clientHeight * dpr;
if (force || width != canvas.width || height != canvas.height) {
renderer.setSize( width, height, false );
camera.aspect = width / height;
camera.updateProjectionMatrix();
}
}
function render(time) {
time *= 0.001; // seconds
resize();
uniforms.iGlobalTime.value = time;
mesh.rotation.x = time * 0.5;
mesh.rotation.y = time * 0.6;
renderer.render(scene, camera);
requestAnimationFrame(render);
}
body { margin: 0; }
canvas { width: 100vw; height: 100vh; display: block; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/86/three.min.js"></script>
请注意,shadertoy着色器通常不设计为用作 Material 。它们效率不高,而更像是一个有趣的 Activity ,“我可以仅使用时间和像素位置作为输入来制作多酷的图像”。因此,虽然效果令人惊讶,但它们通常比传统的 Material (使用纹理)技术慢10倍或100倍甚至1000倍
比较一下this amazing shader,它绘制了整个城市,但至少在我的机器上,它在小窗口中以10-18fps的速度运行,在全屏模式下以1fps的速度运行。以VS为例,例如侠盗猎车手5(Grand Theft Auto 5),它还显示了整个城市,但在同一台机器上全屏显示时却设法以30-60fps的速度运行。
在shadertoy.com上可以学到很多乐趣,可以学到很多有趣的技术,这些技术可能对您的着色器有用,但是请不要误解“生产”技术的用途。由于某种原因,它被称为shaderTOY