我正在第一步编码。我在互联网上做了一些类(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

07-22 16:33