该文章为翻译而来,原文链接见:https://itp-xstory.github.io/p5js-shaders/#/./docs/why-use-shaders
该系列翻译文章将不定期更新,将用于学习记录,若有侵权,请联系我。
1.2 为什么使用shaders?
性能
如果你曾经使用过loadPixels()
、set()
和updatePixels()
函数,甚至在p5.js中做过简单的逐个像素的数学运算,那么你就会意识到以这种方式绘制的局限性。
下面是一个例子,比较了同样的操作(与鼠标的距离)来改变图像中像素亮度,两种不同方式绘制的帧率和渲染速度。首先使用p5中的loadPixels()
,然后使用p5中加载的shader
。
这种性能上的巨大差异( 7 fps vs 150 fps ,取决于机器)是由于p5js中的loadPixels()
函数依次执行填充像素的操作,而shader则是并行执行,更多关于这个问题,请看什么是What are shaders? (下一小节内容)。
视觉上的复杂性
如果你对在浏览器中创建复杂但具有表现力的图形感兴趣,或者是想在p5.js中展现自己的审美,那么shader(和本指南)就是为你准备的!
该案例的代码如下所示。
后续会选取原教程中一部分比较有用的代码放上来,以供大家参考学习。
sketch.js
代码如下所示。
// a shader variable
let theShader;
function preload(){
// load the shader
theShader = loadShader('shader.vert', 'shader.frag');
}
function setup() {
// shaders require WEBGL mode to work
createCanvas(windowWidth, windowHeight, WEBGL);
pixelDensity(1);
noStroke();
}
function draw() {
// shader() sets the active shader with our shader
shader(theShader);
theShader.setUniform("iResolution", [width, height]);
theShader.setUniform("iFrame", frameCount);
theShader.setUniform("iMouse", [mouseX, map(mouseY, 0, height, height, 0)]);
// rect gives us some geometry on the screen
rect(0,0,width, height);
}
function windowResized(){
resizeCanvas(windowWidth, windowHeight);
}
shader.frag
代码如下所示。
// this is a port of "recursive noise experiment" by ompuco
// https://www.shadertoy.com/view/wllGzr
// casey conchinha - @kcconch ( https://github.com/kcconch )
// more p5.js + shader examples: https://itp-xstory.github.io/p5js-shaders/
#ifdef GL_ES
precision mediump float;
#endif
uniform vec2 iResolution;
uniform int iFrame;
uniform vec2 iMouse;
float hash( float n )
{
return fract(sin(n)*43758.5453);
}
float noise( vec3 x )
{
// The noise function returns a value in the range -1.0f -> 1.0f
vec3 p = floor(x);
vec3 f = fract(x);
f = f*f*(3.0-2.0*f);
float n = p.x + p.y*57.0 + 113.0*p.z;
return mix(mix(mix( hash(n+0.0), hash(n+1.0),f.x),
mix( hash(n+57.0), hash(n+58.0),f.x),f.y),
mix(mix( hash(n+113.0), hash(n+114.0),f.x),
mix( hash(n+170.0), hash(n+171.0),f.x),f.y),f.z)-.5;
}
void main()
{
vec3 t = (float(iFrame)*vec3(1.0,2.0,3.0)/1.0)/1000.0;//+iMouse.xyz/1000.0;
// Normalized pixel coordinates (from 0 to 1)
vec2 uv = gl_FragCoord.xy/iResolution.xy;
uv=uv/4.0+.5;
uv-=iMouse.xy/iResolution.xy/4.0;
vec3 col = vec3(0.0);
for(int i = 0; i < 16; i++){
float i2 = float(i)*1.0;
col.r+=noise(uv.xyy*(12.0+i2)+col.rgb+t*sign(sin(i2/3.0)));
col.g+=noise(uv.xyx*(12.0+i2)+col.rgb+t*sign(sin(i2/3.0)));
col.b+=noise(uv.yyx*(12.0+i2)+col.rgb+t*sign(sin(i2/3.0)));
}
for(int i = 0; i < 16; i++){
float i2 = float(i)*1.0;
col.r+=noise(uv.xyy*(32.0)+col.rgb+t*sign(sin(i2/3.0)));
col.g+=noise(uv.xyx*(32.0)+col.rgb+t*sign(sin(i2/3.0)));
col.b+=noise(uv.yyx*(32.0)+col.rgb+t*sign(sin(i2/3.0)));
}
col.rgb/=32.0;
col.rgb=mix(col.rgb,normalize(col.rgb)*2.0,1.0);
col.rgb+=.3;
// Output to screen
gl_FragColor = vec4(col,1.0);
}
shader.vert
代码如下所示。
// vert file and comments from adam ferriss
// https://github.com/aferriss/p5jsShaderExamples
// our vertex data
attribute vec3 aPosition;
// our texcoordinates
attribute vec2 aTexCoord;
void main() {
// copy the position data into a vec4, using 1.0 as the w component
vec4 positionVec4 = vec4(aPosition, 1.0);
positionVec4.xy = positionVec4.xy * 2.0 - 1.0;
// send the vertex information on to the fragment shader
gl_Position = positionVec4;
}