没看过的朋友,记得先去看下入门一。
基础代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
* {
margin: 0;
padding: 0;
}
canvas {
width: 100%;
height: 100%
}
</style>
</head>
<body>
</body>
<script src="./three.js">
</script>
<script>
const vshader = `
void main(){
gl_Position=projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
}
`;
const fshader = `
void main(){
gl_FragColor=vec4(1.0,0.0,0.0,1.0);
}
`;
const scene = new THREE.Scene();
const camera = new THREE.OrthographicCamera(-1, 1, 1, -1, 0.1, 10);
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
const geometry = new THREE.PlaneGeometry(2, 2);
const material = new THREE.ShaderMaterial({
vertexShader: vshader,
fragmentShader: fshader,
});
const plane = new THREE.Mesh(geometry, material);
scene.add(plane);
camera.position.z = 3;
onWindowResize();
animate();
window.addEventListener("resize", onWindowResize, false);
function onWindowResize(event) {
const aspectRatio = window.innerWidth / window.innerHeight;
let width, height;
if (aspectRatio >= 1) {
width = 1;
height = (window.innerHeight / window.innerWidth) * width;
} else {
width = aspectRatio;
height = 1;
}
camera.left = -width;
camera.right = width;
camera.top = height;
camera.bottom = -height;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
function animate() {
requestAnimationFrame(animate);
renderer.render(scene, camera);
}
</script>
</html>
把屏幕切分。
const vshader = `
varying vec2 v_uv;
void main(){
v_uv=uv;
gl_Position=projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
}
`;
const fshader = `
varying vec2 v_uv;
float rect(vec2 pt,vec2 wh,vec2 center){
vec2 rangeMin=center - (wh/2.0);
vec2 rangeMax=center + (wh/2.0);
return step(rangeMin.x,pt.x)*step(pt.x,rangeMax.x)*step(rangeMin.y,pt.y)*step(pt.y,rangeMax.y);
}
void main(){
vec2 xy= fract(v_uv*5.0);
vec3 color=vec3(1.0,0.0,0.0)*rect(xy,vec2(0.5,0.5),vec2(0.5,0.5));
gl_FragColor=vec4(color,1.0);
}
`;
fract(a) 返回小数部分,原理 fract(x)=x-floor(x)
接下来画一个圆 外加一条线
const fshader = `
#define PI 3.14159265359
#define PI2 6.28318530718
varying vec2 v_uv;
float circle(vec2 pt,vec2 rxry,float r ,float strokeWidth, float gradient){
float len=length(rxry-pt);
float distance=abs(r-len);
return smoothstep(strokeWidth/2.0,strokeWidth/2.0-gradient/2.0,distance);
}
float longLine(vec2 pt,vec2 center,float strokeWidth){
vec2 d=pt -center;
vec2 p=vec2(cos(PI/2.0),-sin(PI/2.0));
float l = length(cross(vec3(d,0.0),vec3(p,0.0)))/length(p);
// 两段代码等价
// float l =abs(d.x*p.y-d.y*p.x)/length(p);
return step(l,strokeWidth);
}
void main(){
vec3 color=vec3(1.0,0.0,0.0)*(
circle(v_uv,vec2(0.5),0.3,0.004,0.002)+
longLine(v_uv,vec2(0.5),0.001)
);
gl_FragColor=vec4(color,1.0);
}
`;
函数说明
abs为返回绝对值
cross为叉乘 参数和返回值都说vec3
解释一下 float l =abs(d.x*p.y-d.y*p.x)/length(p);这段代码的含义。 d.x*p.y-d.y*p.x 其实是|d|*|p|*sinθ 在除以一个P的模长,就等于点d到直线P的长度。
接下来画一个长度等于圆半径的直线,线的起始点从圆心开始。
const fshader = `
#define PI 3.14159265359
#define PI2 6.28318530718
varying vec2 v_uv;
float circle(vec2 pt,vec2 rxry,float r ,float strokeWidth, float gradient){
float len=length(rxry-pt);
float distance=abs(r-len);
return smoothstep(strokeWidth/2.0,strokeWidth/2.0-gradient/2.0,distance);
}
float longLine(vec2 pt,vec2 center,float strokeWidth){
vec2 d=pt -center;
vec2 p=vec2(cos(PI/2.0),-sin(PI/2.0));
// float l = length(cross(vec3(d,0.0),vec3(p,0.0)))/length(p);
// 两段代码等价
float l =abs(d.x*p.y-d.y*p.x)/length(p);
return step(l,strokeWidth);
}
float line(vec2 pt,vec2 center,float r,float width,float bianyuan){
vec2 d=pt-center;
vec2 p=vec2(cos(PI/4.0),-sin(PI/4.0))*r;
float d_p=clamp(dot(d,p)/dot(p,p),0.0,1.0);
float h = length(d-p*d_p);
return smoothstep(width,width-bianyuan,h);
}
void main(){
vec3 color=vec3(1.0,0.0,0.0)*(
circle(v_uv,vec2(0.5),0.3,0.004,0.002)+
longLine(v_uv,vec2(0.5),0.001)+
line(v_uv,vec2(0.5),0.3,0.002,0.001)
);
gl_FragColor=vec4(color,1.0);
}
`;
dot点乘 参数是vec2 ,返回值时float;
clamp(a x y) 返回中间大小的值 例如 clamp(5 1 4) 返回的是4 。 -3 1 2返回1 返回三个数的中间数,传参时x必须比y小。
解释一下这段话 float d_p=clamp(dot(d,p)/dot(p,p),0.0,1.0);
dot(d,p)=|d|*|p|*cosθ
dot(d,p)/dot(p,p)=|d|*cosθ/|p| 数学含义就是 d在p上的投影,占p长度的几分之几。
pd_p 就是d投影到p上对应的那个点, length(d-pd_p) 就等于离直线多远.
涉及到一些数学,如果实在看不懂,记住各个函数的用法即可。