shader编程-通过交集、并集、差集实现形状的合并
- 1. 准备
- 2. 交集实现
- 3. 并集实现
- 3. 差集实现
- 4. 示例代码
1. 准备
先复习一下数学概念,交集、并集、差集如下图所示
我们准备拿圆做实验,绘制圆的函数如下
float circle(vec2 st,vec2 center,float radius) {
float blur = 0.002;float pct = distance(st,center);//计算任意点到圆心的距离return 1.0-smoothstep(radius,radius+blur,pct);
}
2. 交集实现
交集是求两个集合相交的部分,实现的方式有两种,一个是使用min函数,另外一种是将二者相乘,结果是一样的,为了验证给出了两个求交集的函数,如下
//交集
float shapeIntersection(float a,float b){
return min(a,b);
}float shapeIntersection1(float a,float b){
return a*b;
}
调用过程是首先绘制了两个圆,然后对这两个圆求交,具体如下
void main( void ) {
//窗口坐标调整为[-1,1],坐标原点在屏幕中心vec2 st = (gl_FragCoord.xy * 2. - u_resolution) / u_resolution.y;vec3 line_color = vec3(1.0,1.0,0.0);vec3 color = vec3(0.6);//背景色float pct = 0.0;//绘制两个圆float cirlce = circle(st,vec2(-0.2,0.0),0.65);float cirlce1 = circle(st,vec2(0.2,0.0),0.65);//交集pct = shapeIntersection(cirlce,cirlce1);//pct = shapeIntersection1(cirlce,cirlce1);color = mix(color,line_color,pct);gl_FragColor = vec4(color, 1);}
结果如下
3. 并集实现
并集是求两个集合所覆盖的全部的部分,实现的方式也有两种,一个是使用max函数,另外一种是将二者相加,如下
//并集
float shapeUnion(float a,float b){
return max(a,b);
}
float shapeUnion1(float a,float b){
return a+b;
}
调用过程非常简单在上一步的基础上直接调用函数求并集即可
//并集
pct = shapeUnion(cirlce,cirlce1);
//pct = shapeUnion1(cirlce,cirlce1);
结果如下
3. 差集实现
差集的实现,其实要先求交集,然后用集合A减去二者的交集就得到A集合相对于B集合的差集,代码如下
//差集
float shapeDifferenceSet(float a,float b){
return a-min(a,b);
}
调用过程就不解释了,直接看执行结果
4. 示例代码
<body><div id="container"></div><script src="http://www.yanhuangxueyuan.com/versions/threejsR92/build/three.js"></script><script>var container;var camera, scene, renderer;var uniforms;var vertexShader = `void main() {gl_Position = vec4( position, 1.0 );} `var fragmentShader = `#ifdef GL_ESprecision mediump float;#endifuniform float u_time;uniform vec2 u_mouse;uniform vec2 u_resolution;float circle(vec2 st,vec2 center,float radius) { float blur = 0.002;float pct = distance(st,center);//计算任意点到圆心的距离return 1.0-smoothstep(radius,radius+blur,pct);} //交集float shapeIntersection(float a,float b){return min(a,b);}float shapeIntersection1(float a,float b){return a*b;}//并集float shapeUnion(float a,float b){return max(a,b);}float shapeUnion1(float a,float b){return a+b;}//差集float shapeDifferenceSet(float a,float b){return a-min(a,b);}void main( void ) {//窗口坐标调整为[-1,1],坐标原点在屏幕中心vec2 st = (gl_FragCoord.xy * 2. - u_resolution) / u_resolution.y;//窗口坐标调整为[0,1],坐标原点在屏幕左下角//vec2 st = gl_FragCoord.xy/u_resolution;vec3 line_color = vec3(1.0,1.0,0.0);vec3 color = vec3(0.6);//背景色float pct = 0.0;//绘制两个圆float cirlce = circle(st,vec2(-0.2,0.0),0.65);float cirlce1 = circle(st,vec2(0.2,0.0),0.65);//交集pct = shapeIntersection(cirlce,cirlce1);pct = shapeIntersection1(cirlce,cirlce1);//并集pct = shapeUnion(cirlce,cirlce1);//pct = shapeUnion1(cirlce,cirlce1);//差集pct = shapeDifferenceSet(cirlce,cirlce1);color = mix(color,line_color,pct);gl_FragColor = vec4(color, 1);}`init();animate();function init() {
container = document.getElementById('container');camera = new THREE.Camera();camera.position.z = 1;scene = new THREE.Scene();var geometry = new THREE.PlaneBufferGeometry(2, 2);uniforms = {
u_time: {
type: "f",value: 1.0},u_resolution: {
type: "v2",value: new THREE.Vector2()},u_mouse: {
type: "v2",value: new THREE.Vector2()}};var material = new THREE.ShaderMaterial({
uniforms: uniforms,vertexShader: vertexShader,fragmentShader: fragmentShader});var mesh = new THREE.Mesh(geometry, material);scene.add(mesh);renderer = new THREE.WebGLRenderer();//renderer.setPixelRatio(window.devicePixelRatio);container.appendChild(renderer.domElement);onWindowResize();window.addEventListener('resize', onWindowResize, false);document.onmousemove = function (e) {
uniforms.u_mouse.value.x = e.pageXuniforms.u_mouse.value.y = e.pageY}}function onWindowResize(event) {
renderer.setSize(800, 800);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>