该文章为翻译而来,原文链接传送门
该系列翻译文章将不定期更新,将用于学习记录,若有侵权,请联系我。
1.1 欢迎来到p5js shader教程
着色器(shader)是为你的项目绘制图形的一个好方法。到目前为止,你可能已经绘制了像rect()或ellipse()这样的2D图形,甚至可能是像plane()或box()这样的3D图形,而且你应该非常熟悉使用fill()来决定这些对象的颜色。但如果我们能让这些对象上有移动的图形且不会让我们的草图绘制速度变慢,那不是更好吗?
欢迎进入shader的世界!
index.html代码如下所示。
<!-- This is a static file -->
<!-- served from your routes in server.js -->
<!DOCTYPE html>
<html>
<head>
<title>shader as texture</title>
<meta name="description" content="p5.js CDN Template">
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.6.0/p5.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.6.0/addons/p5.dom.js"></script>
<script src="sketch.js"></script>
</head>
<body>
<header>
<!-- <h1>
shader as texture
</h1> -->
</header>
<div class="glitchButton" style="position:fixed;top:20px;right:20px;"></div>
<script src="https://button.glitch.me/button.js" data-style="glitch"></script>
</body>
</html>
sketch.js代码如下所示。
/*
* @name Applying Shaders as Textures
* @description Shaders can be applied to 2D/3D shapes as textures.
* To learn more about shaders and p5.js: https://itp-xstory.github.io/p5js-shaders/
*/
let theShader;
let shaderTexture;
let theta = 0;
let x;
let y;
let outsideRadius = 200;
let insideRadius = 100;
function preload(){
// load the shader
theShader = loadShader('texture.vert','texture.frag');
}
function setup() {
// disables scaling for retina screens which can create inconsistent scaling between displays
//pixelDensity(1);
// shaders require WEBGL mode to work
createCanvas(710, 400, WEBGL);
noStroke();
// initialize the createGraphics layers
shaderTexture = createGraphics(710, 400, WEBGL);
// turn off the createGraphics layers stroke
shaderTexture.noStroke();
x = -50;
y = 0;
}
function draw() {
// instead of just setting the active shader we are passing it to the createGraphics layer
shaderTexture.shader(theShader);
// here we're using setUniform() to send our uniform values to the shader
theShader.setUniform("resolution", [width, height]);
theShader.setUniform("time", millis() / 1000.0);
theShader.setUniform("mouse", [mouseX, map(mouseY, 0, height, height, 0)]);
// passing the shaderTexture layer geometry to render on
shaderTexture.rect(0,0,width,height);
background(255);
//pass the shader as a texture
texture(shaderTexture);
translate(-150, 0, 0);
push();
rotateZ(theta * mouseX * 0.0001);
rotateX(theta * mouseX * 0.0001);
rotateY(theta * mouseX * 0.0001);
theta += 0.05;
sphere(125);
pop();
/* when you put a texture or shader on an ellipse it is rendered in 3d,
so a fifth parameter that controls the # vertices in it becomes necessary,
or else you'll have sharp corners. setting it to 100 is smooth. */
let ellipseFidelity = int(map(mouseX, 0, width, 8, 100));
ellipse(260, 0, 200, 200, ellipseFidelity);
}
texture.frag代码如下所示。
// casey conchinha - @kcconch ( https://github.com/kcconch )
// louise lessel - @louiselessel ( https://github.com/louiselessel )
// more p5.js + shader examples: https://itp-xstory.github.io/p5js-shaders/
// rotate/tile functions from example by patricio gonzalez vivo
// @patriciogv ( patriciogonzalezvivo.com )
#ifdef GL_ES
precision mediump float;
#endif
#define PI 3.14159265358979323846
uniform vec2 resolution;
uniform float time;
uniform vec2 mouse;
vec2 rotate2D (vec2 _st, float _angle) {
_st -= 0.5;
_st = mat2(cos(_angle),-sin(_angle),
sin(_angle),cos(_angle)) * _st;
_st += 0.5;
return _st;
}
vec2 tile (vec2 _st, float _zoom) {
_st *= _zoom;
return fract(_st);
}
vec2 rotateTilePattern(vec2 _st){
// Scale the coordinate system by 2x2
_st *= 2.0;
// Give each cell an index number
// according to its position
float index = 0.0;
index += step(1., mod(_st.x,2.0));
index += step(1., mod(_st.y,2.0))*2.0;
// |
// 2 | 3
// |
//--------------
// |
// 0 | 1
// |
// Make each cell between 0.0 - 1.0
_st = fract(_st);
// Rotate each cell according to the index
if(index == 1.0){
// Rotate cell 1 by 90 degrees
_st = rotate2D(_st,PI*0.5);
} else if(index == 2.0){
// Rotate cell 2 by -90 degrees
_st = rotate2D(_st,PI*-0.5);
} else if(index == 3.0){
// Rotate cell 3 by 180 degrees
_st = rotate2D(_st,PI);
}
return _st;
}
float concentricCircles(in vec2 st, in vec2 radius, in float res, in float scale) {
float dist = distance(st,radius);
float pct = floor(dist*res)/scale;
return pct;
}
void main (void) {
vec2 st = gl_FragCoord.xy/resolution.xy;
vec2 mst = gl_FragCoord.xy/mouse.xy;
float mdist= distance(vec2(1.0,1.0), mst);
float dist = distance(st,vec2(sin(time/10.0),cos(time/10.0)));
st = tile(st,10.0);
st = rotate2D(st,dist/(mdist/5.0)*PI*2.0);
gl_FragColor = vec4(vec3(concentricCircles(st, vec2(0.0,0.0), 5.0, 5.0),concentricCircles(st, vec2(0.0,0.0), 10.0, 10.0),concentricCircles(st, vec2(0.0,0.0), 20.0, 10.0)),1.0);
}
texture.vert代码如下所示。
// vert file and comments from adam ferriss
// https://github.com/aferriss/p5jsShaderExamples
// our vertex data
attribute vec3 aPosition;
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;
}
在本指南中,你将了解什么是shader,如何在p5js中有效地使用它,以及如何制作你自己的shader。
请将此视为 “shader的小册子”。这是一个让你开始在p5js中为你的项目绘制精彩图形的指导教程,也是对shader的简单介绍。然后,如果你发现自己被shader的世界迷住了(就像我们一样),请看关于进一步使用shader的部分(在后面章节),在那里我们收集了一些在线寻找shader和学习更多知识的最佳来源。
其中,Patricio Gonzalez Vivo和Jen Lowe的 “The Book of Shaders” 是一个真正伟大的资源,如果你想更深入地了解shader的技术细节,我们将从这本在线书籍中借用一些解释。
致谢
本指南是由Casey Conchinha和Louise Lessél在 ITP NYU的xStory期间制作的一个开源项目。
我们要感谢以下人士在项目中提供的帮助和指导。
Adam Ferriss,用他自己的p5 shader github repo为整个项目奠定了基础。
Or Fleisher、Kyle Phillips和Sehyun Kim,他们是我们的导师。
Stalgia Grigg和Kate Hollenbach回答了我们的p5.js着色器实现问题。
Patricio Gonzalez Vivo和Jen Lowe编写了令人惊叹的《The Book of Shaders》。