shader编程-三维场景下SDF建模,平滑交集平滑并集平滑差集(WebGL-Shader开发基础11)
Posted 点燃火柴
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了shader编程-三维场景下SDF建模,平滑交集平滑并集平滑差集(WebGL-Shader开发基础11)相关的知识,希望对你有一定的参考价值。
三维场景下SDF建模,平滑交集、平滑并集、平滑差集
1. demo效果
smooth-operate
如上所示,三个展示的模型分别为球体和立方体进行平滑交集、平滑并集、平滑差集运算的效果
2. 实现要点
2.1 平滑运算方法定义
demo用到平滑交集、平滑并集、平滑差集运算,它们的定义如下
//平滑交集
float opSmoothI( float d1, float d2, float k )
float h = max(k-abs(d1-d2),0.0);
return max(d1, d2) + h*h*0.25/k;
//平滑并集
float opSmoothU( float d1, float d2, float k )
float h = max(k-abs(d1-d2),0.0);
return min(d1, d2) - h*h*0.25/k;
//平滑差集
float opSmoothS( float d1, float d2, float k )
float h = max(k-abs(-d1-d2),0.0);
return max(-d1, d2) + h*h*0.25/k;
2.2 模型计算
有了之前的铺垫,今天的内容比较简单,就是将两个模型使用对应的平滑操作函数处理,然后使用处理好的结果追加上材质ID,之后把它们求并集返回即可,具体如下
vec2 res = vec2(p.y,0.0);//地面
vec3 pos = p-vec3(0,2,5);//确定模型的中心
vec4 boxPos = vec4(pos,1.0);//转为其次坐标
boxPos*=rotX(0.6);//旋转
//平滑交集
boxPos.x += 2.5; //使模型绘制原点沿x轴左移2.5个单位
float box1 = sdBox(boxPos.xyz,vec3(1.0),0.06);//方块
float sphere1 = sdSphere(boxPos.xyz-vec3(0.0,sin(u_time)*0.25+1.2,0.0),0.6);//上下浮动的球
vec2 boxSphereSmoothI = vec2(opSmoothI(sphere1,box1,0.25),1.0);
//平滑并集
boxPos.x -= 2.5;//使模型绘制原点沿x轴右移2.5个单位
float box2 = sdBox(boxPos.xyz,vec3(1.0),0.06);//方块
float sphere2 = sdSphere(boxPos.xyz-vec3(0.0,sin(u_time)*0.25+1.2,0.0),0.6);//上下浮动的球
vec2 boxSphereSmoothU = vec2(opSmoothU(sphere2,box2,0.25),2.0);
//平滑差集
boxPos.x -= 2.5;//使模型绘制原点沿x轴右移2.5个单位
float box = sdBox(boxPos.xyz,vec3(1.0),0.06);//方块
float sphere = sdSphere(boxPos.xyz-vec3(0.0,sin(u_time)*0.25+1.2,0.0),0.6);//上下浮动的球
vec2 boxSphereSmoothS = vec2(opSmoothS(sphere,box,0.25),3.0);
res = opU(res,boxSphereSmoothI);
res = opU(res,boxSphereSmoothU);
res = opU(res,boxSphereSmoothS);
return res;
3. demo代码
今天新内容不但少而且简单,只经过两步就跳到最后的代码展示了,愿天下没有难写的shader!
<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_ES
precision mediump float;
#endif
uniform float u_time;
uniform vec2 u_mouse;
uniform vec2 u_resolution;
const int MAX_STEPS = 100;//最大步进步数
const float MAX_DIST = 100.0;//最大步进距离
const float SURF_DIST = 0.01;//相交检测临近表面距离
//绕z轴旋转矩阵
mat4 rotZ(float a)
return mat4(cos(a),-sin(a),0.0,0.0,
sin(a),cos(a),0.0,0.0,
0.0,0.0,1.0,0.0,
0.0,0.0,0.0,1.0
);
//绕x轴旋转矩阵
mat4 rotX(float a)
return mat4(1.0,0.0,0.0,0.0,
0.0,cos(a),-sin(a),0.0,
0.0,sin(a),cos(a),0.0,
0.0,0.0,0.0,1.0
);
//绕y轴旋转矩阵
mat4 rotY(float a)
return mat4(cos(a),0.0,sin(a),0.0,
0.0,1.0,0.0,0.0,
-sin(a),0.0,cos(a),0.0,
0.0,0.0,0.0,1.0
);
//平滑交集
float opSmoothI( float d1, float d2, float k )
float h = max(k-abs(d1-d2),0.0);
return max(d1, d2) + h*h*0.25/k;
//平滑并集
float opSmoothU( float d1, float d2, float k )
float h = max(k-abs(d1-d2),0.0);
return min(d1, d2) - h*h*0.25/k;
//平滑差集
float opSmoothS( float d1, float d2, float k )
float h = max(k-abs(-d1-d2),0.0);
return max(-d1, d2) + h*h*0.25/k;
//并集
vec2 opU( vec2 d1, vec2 d2 )
return (d1.x<d2.x) ? d1 : d2;
//球体
float sdSphere( vec3 p, float s )
return length(p)-s;
//立方体
float sdBox( vec3 p, vec3 b,float rad )
vec3 d = abs(p) - b;
return min(max(d.x,max(d.y,d.z)),0.0) + length(max(d,0.0)) - rad;
vec2 getDistandMaterial(vec3 p)
vec2 res = vec2(p.y,0.0);//地面
vec3 pos = p-vec3(0,2,5);//确定模型的中心
vec4 boxPos = vec4(pos,1.0);//转为其次坐标
boxPos*=rotX(0.6);//旋转
//平滑交集
boxPos.x += 2.5; //使模型绘制原点沿x轴左移2.5个单位
float box1 = sdBox(boxPos.xyz,vec3(1.0),0.06);//方块
float sphere1 = sdSphere(boxPos.xyz-vec3(0.0,sin(u_time)*0.25+1.2,0.0),0.6);//上下浮动的球
vec2 boxSphereSmoothI = vec2(opSmoothI(sphere1,box1,0.25),1.0);
//平滑并集
boxPos.x -= 2.5;//使模型绘制原点沿x轴右移2.5个单位
float box2 = sdBox(boxPos.xyz,vec3(1.0),0.06);//方块
float sphere2 = sdSphere(boxPos.xyz-vec3(0.0,sin(u_time)*0.25+1.2,0.0),0.6);//上下浮动的球
vec2 boxSphereSmoothU = vec2(opSmoothU(sphere2,box2,0.25),2.0);
//平滑差集
boxPos.x -= 2.5;//使模型绘制原点沿x轴右移2.5个单位
float box = sdBox(boxPos.xyz,vec3(1.0),0.06);//方块
float sphere = sdSphere(boxPos.xyz-vec3(0.0,sin(u_time)*0.25+1.2,0.0),0.6);//上下浮动的球
vec2 boxSphereSmoothS = vec2(opSmoothS(sphere,box,0.25),3.0);
res = opU(res,boxSphereSmoothI);
res = opU(res,boxSphereSmoothU);
res = opU(res,boxSphereSmoothS);
return res;
vec2 rayMarch(vec3 rayStart, vec3 rayDirection)
float depth=0.;
float material=0.;
for(int i=0; i<MAX_STEPS; i++)
vec3 p = rayStart + rayDirection*depth;//上一次步进结束后的坐标也就是这一次步进出发点
vec2 dm = getDistandMaterial(p);
float dist = dm.x;//获取当前步进出发点与物体相交时距离
material = dm.y;
depth += dist; //步进长度累加
if(depth>MAX_DIST || dist<SURF_DIST) break;//步进距离大于最大步进距离或与物体表面距离小于最小表面距离(光线进入物体)停止前进
return vec2(depth,material);
vec3 getNormal(vec3 p)
return normalize(vec3(
getDistandMaterial(vec3(p.x + SURF_DIST, p.y, p.z)).x - getDistandMaterial(vec3(p.x - SURF_DIST, p.y, p.z)).x,
getDistandMaterial(vec3(p.x, p.y + SURF_DIST, p.z)).x - getDistandMaterial(vec3(p.x, p.y - SURF_DIST, p.z)).x,
getDistandMaterial(vec3(p.x, p.y, p.z + SURF_DIST)).x - getDistandMaterial(vec3(p.x, p.y, p.z - SURF_DIST)).x
));
//Blinn-Phong模型光照计算
vec3 calcBlinnPhongLight( vec3 materialColor, vec3 p, vec3 ro)
vec3 lightPos = vec3(5.0 * sin(u_time), 20.0, 10.0*cos(u_time)-18.);//光源坐标
//计算环境光
float k_a = 0.3;//环境光反射系数
vec3 ambientLight = 0.6 * vec3(1.0, 1.0, 1.0);
vec3 ambient = k_a*ambientLight;
vec3 N = getNormal(p); //法线
vec3 L = normalize(lightPos - p); //光照方向
vec3 V = normalize(ro - p); //视线
vec3 H = normalize(V+L); //半程向量
float r = length(lightPos - p);
//计算漫反射光
float k_d = 0.6;//漫反射系数
float dotLN = clamp(dot(L, N),0.0,1.0);//点乘,并将结果限定在0~1
vec3 diffuse = k_d * (materialColor/r*r) * dotLN;
//计算高光反射光
float k_s = 0.8;//镜面反射系数
float shininess = 160.0;
vec3 specularColor = vec3(1.0, 1.0, 1.0);
vec3 specular = k_s * (specularColor/r*r)* pow(clamp(dot(N, H), 0.0, 1.0), shininess);//计算高光
//计算阴影
vec2 res = rayMarch(p + N*SURF_DIST*2.0,L);
if(res.x<length(lightPos-p)-0.001)
diffuse*=0.1;
//颜色 = 环境光 + 漫反射光 + 镜面反射光
return ambient +diffuse + specular;
void main( void )
//窗口坐标调整为[-1,1],坐标原点在屏幕中心
vec2 st = (gl_FragCoord.xy * 2. - u_resolution) / u_resolution.y;
vec3 ro = vec3(0.0,2.0,0.0);//视点
vec3 rd = normalize(vec3(st.x,st.y,1.0));//视线方向
vec2 res = rayMarch(ro,rd);//反向光线追踪求交点距离与材质ID
float d = res.x;//物体与视点的距离
float m = res.y;//材质ID
vec3 p = ro + rd * d;
vec3 materialColor = vec3(1.0, 0.0, 1.0);//默认材质色,使用差集计算出来的内壁会使用该色填充
//为不同物体设置不同的材质颜色
if(m==0.0)
materialColor = vec3(.2, 0.0, 0.0);
if(m==1.0)
materialColor = vec3(.2, 0.0, 1.0);
if(m==2.0)
materialColor = vec3(.7, 0.2, 0.0);
if(m==3.0)
materialColor = vec3(.8, .9, 0.0);
vec3 color = vec3(1.0,1.0,1.0);
//使用Blinn-Phong模型计算光照
color *= calcBlinnPhongLight( materialColor, p, ro);
gl_FragColor = vec4(color, 1.0);
`
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.pageX
uniforms.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>
以上是关于shader编程-三维场景下SDF建模,平滑交集平滑并集平滑差集(WebGL-Shader开发基础11)的主要内容,如果未能解决你的问题,请参考以下文章
shader编程-三维场景下SDF建模,平滑交集平滑并集平滑差集(WebGL-Shader开发基础11)
shader编程-三维场景下SDF建模,平滑交集平滑并集平滑差集(WebGL-Shader开发基础11)
shader编程-三维场景下SDF建模,对模型进行扭曲弯曲裁剪掏空操作(WebGL-Shader开发基础12)
shader编程-三维场景下使用交集并集差集方法CSG建模(WebGL-Shader开发基础10)