使用 shader 绘制 五角星线段,mix 合并图层
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>五角星线段</title>
<script src="../external/three.js"></script>
<script src="../controls/OrbitControls.js"></script>
<style>
body {
overflow: hidden;
padding: 0;
margin: 0;
}
</style>
</head>
<body>
<div id="container"></div>
<script id="vertexShader" type="x-shader/x-vertex">
varying vec2 pos;
void main() {
pos = (vec2(position) + 1.0) * 0.5;
mat4 mvp = projectionMatrix * modelViewMatrix;
gl_Position = mvp * vec4( position, 1.0 );
}
</script>
<script id="fragmentShader" type="x-shader/x-fragment">
uniform vec2 u_resolution;
uniform float u_time;
varying vec2 pos;
vec4 _OutlineColor = vec4(1.0,1.0,1.0,1.0);
vec4 _FrontColor = vec4(1.0,0.0,0.0,1.0);
float pi=3.14159;
float _Antialias=0.01;
//画点
vec4 circle(vec2 pos, vec2 center, float radius, vec4 color) {
//求点是否在圆的半径内
float d = length(pos - center) - radius;
//fwidth(x) ==abs(ddx(x)) + abs(ddy(x)),对点求偏导,这种处理能让数据变平滑
float w = fwidth(0.5*d) * 2.0;
//图层0 画圆外边框
vec4 layer0 = vec4(_OutlineColor.rgb, 1.0-smoothstep(-w, w, d - _Antialias));
//图层1 画内圆
vec4 layer1 = vec4(color.rgb, 1.0-smoothstep(0.0, w, d));
//混合两个图层并返回
return mix(layer0, layer1, layer1.a);
// return layer0;
}
//画线
vec4 line(vec2 pos, vec2 point1, vec2 point2, float width) {
//分别求出点二到点一以及当前点到点一的向量
vec2 dir0 = point2 - point1;
vec2 dir1 = pos - point1;
//dot()方法返回两个向量的点积 如果向量垂直返回0,平行返回1 相反返回-1
//clamp()方法限制返回0到1 截出线段,不然会返回直线
//这公式返回点到线上的距离
float h = clamp(dot(dir1, dir0)/dot(dir0, dir0), 0.0, 1.0);
//判断点是否在线的两边范围内
float d = (length(dir1 - dir0 * h) - width * 0.5);
//平滑处理
float w = fwidth(0.5*d) * 2.0;
//画线的外边
vec4 layer0 = vec4(_OutlineColor.rgb, 1.-smoothstep(-w, w, d - _Antialias));
//画线
vec4 layer1 = vec4(_FrontColor.rgb, 1.-smoothstep(-w, w, d));
//混合两个图层
return mix(layer0, layer1, layer1.a);
}
//根据index来保存图层的颜色值
void setlayer(inout vec4 layer[5],int index,vec4 val){
if(index==0)
layer[0]=val;
if(index==1)
layer[1]=val;
if(index==2)
layer[2]=val;
if(index==3)
layer[3]=val;
if(index==4)
layer[4]=val;
}
void main() {
vec2 uv = pos * 3.0 - 1.5;
//动态背景颜色
vec3 col = 0.5 + 0.5*cos(u_time+uv.xyx+vec3(0.0, 2.0, 4.0));
vec4 fragColor=vec4(col,1.0);
//点的图层
vec4 layers[5];
float d=u_time*10.0;
//保存五个点 从1开始
vec2 degree[6];
//for循环创建五个点
for(int i=0;i<=4;i++){
//保存点
//坐标上圆边上的点的坐标(cos(r),sin(r)) r为弧度
degree[i+1]=vec2(cos(d*pi/180.0),sin((d*pi)/180.0));
//绘制点
setlayer(layers, i, circle(uv, degree[i+1], 0.06, _FrontColor) );
//圆上的五角星,每个点相隔72度
d+=72.0;
}
//for循环画五条线
for(int i=1;i<6;i++){
vec2 point1=vec2(0.0,0.0);
//判断连线的位置 即当前点的隔一个点
if(i<=2) {
point1=degree[i+3];
} else {
point1=degree[i-2];
}
//画线
vec4 temp=line(uv,degree[i],point1,0.02);
//混合线的图层
fragColor=mix(fragColor, temp, temp.a);
}
//混合点的图层
for (int i = 4; i >= 0; i--) {
fragColor = mix(fragColor, layers[i], layers[i].a);
}
gl_FragColor = fragColor;
}
</script>
<script>
//https://blog.csdn.net/ssssssilver/article/details/81166233
var container;
var camera, scene, renderer;
var uniforms;
init();
animate();
function init() {
container = document.getElementById( 'container' );
camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );
camera.position.set(0, 0, 2);
scene = new THREE.Scene();
scene.add(new THREE.AxesHelper(20));
var geometry = new THREE.PlaneBufferGeometry( 2, 2 );
uniforms = {
u_time: { type: "f", value: 1.0 },
u_resolution: { type: "v2", value: new THREE.Vector2(window.innerWidth, window.innerHeight) }
};
var material = new THREE.ShaderMaterial( {
uniforms: uniforms,
side: THREE.DoubleSide,
vertexShader: document.getElementById( 'vertexShader' ).textContent,
fragmentShader: document.getElementById( 'fragmentShader' ).textContent
} );
material.extensions.derivatives = true;
var material2 = new THREE.MeshBasicMaterial({color: '#00bbbb', wireframe: true, side: THREE.DoubleSide})
var mesh = new THREE.Mesh( geometry, material );
scene.add( mesh );
renderer = new THREE.WebGLRenderer();
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setClearColor( '#f4f4f4', 1 );
container.appendChild( renderer.domElement );
let orbit = new THREE.OrbitControls( camera, renderer.domElement );
onWindowResize();
window.addEventListener( 'resize', onWindowResize, false );
}
function onWindowResize( event ) {
renderer.setSize( window.innerWidth, window.innerHeight );
// uniforms.u_resolution.value.x = renderer.domElement.width;
// uniforms.u_resolution.value.y = renderer.domElement.height;
}
function animate() {
requestAnimationFrame( animate );
render();
}
function render() {
uniforms.u_time.value += 0.02;
renderer.render( scene, camera );
}
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>雨伞</title>
<script src="../lib/three.js"></script>
<script src="../lib/OrbitControls.js"></script>
<style>
body {
overflow: hidden;
padding: 0;
margin: 0;
}
</style>
</head>
<body>
<div id="container"></div>
<script id="vertexShader" type="x-shader/x-vertex">
varying vec2 pos;
void main() {
pos = (vec2(position) + 1.0) * 0.5;
mat4 mvp = projectionMatrix * modelViewMatrix;
gl_Position = mvp * vec4( position, 1.0 );
}
</script>
<script id="fragmentShader" type="x-shader/x-fragment">
uniform vec2 u_resolution;
uniform float u_time;
varying vec2 pos;
float sdfCircle(vec2 center, float radius, vec2 coord ) {
vec2 offset = coord - center;
return sqrt((offset.x * offset.x) + (offset.y * offset.y)) - radius;
}
float sdfEllipse(vec2 center, float a, float b, vec2 coord) {
float a2 = a * a;
float b2 = b * b;
return (b2 * (coord.x - center.x) * (coord.x - center.x) +
a2 * (coord.y - center.y) * (coord.y - center.y) - a2 * b2)/(a2 * b2);
}
float sdfLine(vec2 p0, vec2 p1, float width, vec2 coord) {
vec2 dir0 = p1 - p0;
vec2 dir1 = coord - p0;
float h = clamp(dot(dir0, dir1)/dot(dir0, dir0), 0.0, 1.0);
return (length(dir1 - dir0 * h) - width * 0.5);
}
float sdfUnion( const float a, const float b ) {
return min(a, b);
}
float sdfDifference( const float a, const float b) {
return max(a, -b);
}
float sdfIntersection( const float a, const float b ) {
return max(a, b);
}
vec4 render(float d, vec3 color, float stroke) {
//stroke = fwidth(d) * 2.0;
float anti = fwidth(d) * 1.0;
vec4 strokeLayer = vec4(vec3(0.05), 1.0-smoothstep(-anti, anti, d - stroke));
vec4 colorLayer = vec4(color, 1.0-smoothstep(-anti, anti, d));
if (stroke < 0.000001) {
return colorLayer;
}
return vec4(mix(strokeLayer.rgb, colorLayer.rgb, colorLayer.a), strokeLayer.a);
}
void main() {
float pixSize = 1.0 / 512.0;
vec2 uv = pos;
float stroke = pixSize * 1.5;
vec2 center = vec2(0.5, 0.5 );
float a = sdfEllipse(vec2(0.5, center.y*2.0-0.34), 0.25, 0.25, uv);
float b = sdfEllipse(vec2(0.5, center.y*2.0+0.03), 0.8, 0.35, uv);
b = sdfIntersection(a, b);
vec4 layer1 = render(b, vec3(0.32, 0.56, 0.53), fwidth(b) * 2.0);
// Draw strips
vec4 layer2 = layer1;
float t, r0, r1, r2, e, f;
vec2 sinuv = vec2(uv.x, (sin(uv.x*40.0)*0.02 + 1.0)*uv.y);
for (float i = 0.0; i < 10.0; i++) {
t = mod(3.14 + 0.3 * i, 3.0) * 0.2;
r0 = (t - 0.15) / 0.2 * 0.9 + 0.1;
r1 = (t - 0.15) / 0.2 * 0.1 + 0.9;
r2 = (t - 0.15) / 0.2 * 0.15 + 0.85;
e = sdfEllipse(vec2(0.5, center.y*2.0+0.37-t*r2), 0.7*r0, 0.35*r1, sinuv);
f = sdfEllipse(vec2(0.5, center.y*2.0+0.41-t), 0.7*r0, 0.35*r1, sinuv);
f = sdfDifference(e, f);
f = sdfIntersection(f, b);
vec4 layer = render(f, vec3(1.0, 0.81, 0.27), 0.0);
layer2 = mix(layer2, layer, layer.a);
}
// Draw the handle
float bottom = 0.08;
float handleWidth = 0.01;
float handleRadius = 0.04;
float d = sdfCircle(vec2(0.5-handleRadius+0.5*handleWidth, bottom), handleRadius, uv);
float c = sdfCircle(vec2(0.5-handleRadius+0.5*handleWidth, bottom), handleRadius-handleWidth, uv);
d = sdfDifference(d, c);
c = uv.y - bottom;
d = sdfIntersection(d, c);
c = sdfLine(vec2(0.5, center.y*2.0-0.05), vec2(0.5, bottom), handleWidth, uv);
d = sdfUnion(d, c);
c = sdfCircle(vec2(0.5, center.y*2.0-0.05), 0.01, uv);
d = sdfUnion(c, d);
c = sdfCircle(vec2(0.5-handleRadius*2.0+handleWidth, bottom), handleWidth*0.5, uv);
d = sdfUnion(c, d);
vec4 layer0 = render(d, vec3(0.404, 0.298, 0.278), stroke);
vec2 p = 2.0*uv - 1.0;
vec3 bcol = vec3(1.0,0.8,0.7-0.07*p.y)*(1.0-0.25*length(p));
vec4 fragColor = vec4(bcol, 1.0);
fragColor.rgb = mix(fragColor.rgb, layer0.rgb, layer0.a);
fragColor.rgb = mix(fragColor.rgb, layer1.rgb, layer1.a);
fragColor.rgb = mix(fragColor.rgb, layer2.rgb, layer2.a);
fragColor.rgb = pow(fragColor.rgb, vec3(1.0/2.2));
gl_FragColor = fragColor;
}
</script>
<script>
//https://blog.csdn.net/ssssssilver/article/details/81166233
var container;
var camera, scene, renderer;
var uniforms;
init();
animate();
function init() {
container = document.getElementById( 'container' );
camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );
camera.position.set(0, 0, 2);
scene = new THREE.Scene();
scene.add(new THREE.AxesHelper(20));
var geometry = new THREE.PlaneBufferGeometry( 2, 2 );
uniforms = {
u_time: { type: "f", value: 1.0 },
u_resolution: { type: "v2", value: new THREE.Vector2(window.innerWidth, window.innerHeight) }
};
var material = new THREE.ShaderMaterial( {
uniforms: uniforms,
side: THREE.DoubleSide,
vertexShader: document.getElementById( 'vertexShader' ).textContent,
fragmentShader: document.getElementById( 'fragmentShader' ).textContent
} );
material.extensions.derivatives = true;
var material2 = new THREE.MeshBasicMaterial({color: '#00bbbb', wireframe: true, side: THREE.DoubleSide})
var mesh = new THREE.Mesh( geometry, material );
scene.add( mesh );
renderer = new THREE.WebGLRenderer();
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setClearColor( '#f4f4f4', 1 );
container.appendChild( renderer.domElement );
let orbit = new THREE.OrbitControls( camera, renderer.domElement );
onWindowResize();
window.addEventListener( 'resize', onWindowResize, false );
}
function onWindowResize( event ) {
renderer.setSize( window.innerWidth, window.innerHeight );
// uniforms.u_resolution.value.x = renderer.domElement.width;
// uniforms.u_resolution.value.y = renderer.domElement.height;
}
function animate() {
requestAnimationFrame( animate );
render();
}
function render() {
uniforms.u_time.value += 0.02;
renderer.render( scene, camera );
}
</script>
</body>
</html>
https://sites.google.com/site/webglbook/