webGL clipping plane和doubleside
来源:互联网 发布:linux shell 创建文件 编辑:程序博客网 时间:2024/06/05 22:33
完整的例子 clippingplane
1.几何基础知识
1)已知平面上一点和此点处法向量可以确定平面。不妨设这一点是r0,法向量是n,对平面任一点r有
计算得
整理得
可见一个平面可以用[a,b,c,d]表示。
2)点到平面的距离公式是
标准化法向量则
整理得
再去掉绝对值,则当D<0时点在平面法向量所指一边,D>0时在另一边,D=0时在平面上。
2.利用openGL的内建变量
openGL在vertexShader里有一个内建数组变量 gl_ClipDistance。可以用它存放当前点到平面的距离。因为可能有多个平面,所以它是数组变量。vertexShader处理当前点时,计算出它距平面A的距离可以存放在 gl_ClipDistance[0]里,与平面B的距离存在gl_ClipDistance[1]里,如此类推。这些值在光栅化时会线性插值,小于0的fragment不会传给fragmentShader,从而实现clipping。 这里openGL认为与平面距离小于0的点会被cull,它认为法线方向为正方向。这是人为的规定,不过控制权在流水线不可编程的图元装配和光栅化部分,所以不可以改变。下面将看到,在webGL里,在fragmentShader里编程实现对fragment的选取,这样既可以选择距离大于0的点,也可以相反。不过为了统一,选取法线方向为正方向是一个好选择。
//opengl的可选做法#version 330 core//vec4(a,b,c,d)表示一个平面,为了计算方便(a,b,c)一般已经标准化,注意对一个平面法向量进行标准化时,d也要相应变化float gl_ClipDistance[1];//声明内建数组的长度为1,表示只有一个clipping planeuniform vec4 Plane;{ //计算出当前点距离平面的距离存在数组第0位置 gl_ClipDistance[0]=计算出来的距离}
2.webGL的做法
因为webGL不支持这个内建变量,所以不能用这个方法。可以在vertexShader里计算点到平面的距离,再声明自己的varying变量来记录距离,这个变量会插值赋给每一个fragment。在fragmentShader里根据这个值来决定是否clipping当前fragment。没有内建变量就声明varying变量,只不过对fragment的选取不在光栅化部分,无论这个值的大小所有fragment都将传给fragmentShader处理,所以要在fragmengShader里编程实现。
计算距离也可以放到fragmentShader里,这样vertexShader只要将点的位置传给fragmentShader。此时插值的是点的位置不再是距离。例子里将选取这一做法。
要注意的是计算距离时平面和点要在同一的坐标系下,这里不妨选取view坐标系。传给fragmentShader用来表示平面的uniform变量vec4(a,b,c,d)为了方便不妨选取model坐标系,然后在fragmentShader里对它进行转变。为了得到view坐标系下的平面表示,我们需要知道在model坐标系下,平面上一点的坐标和这一点处的法向量,再将这一点和法向量转为view坐标系下的表示。就像将在model坐标系里的点转为view坐标系里的点是用ViewMatrix*ModelMatrix*P一样。ViewMatrix*ModelMatrix*r0得到r0在view坐标系下的坐标,而根据ViewMatrix*ModelMatrix矩阵的左上3*3矩阵的逆的转置得到的矩阵可以将法向量(a,b,c)转为view坐标系下的表示,这叫法向量转换矩阵,具体的数学推导差不多每一本计算机图像入门书籍都有,网上也随处可见。
现在问题是,根据传进来的vec4(a,b,c,d),可以知道法向量是vec3(a,b,c),因为平面上任何点处的法向量都一样,所以r0处法向量也是vec3(a,b,c),但是怎么找出一个r0?这里根据法向量已经标准化即a*a+b*b+c*c=1和这两个事实,可以得知点vec3(-ad,-bd,-cd)在平面上。
<script id="vertexShader" type="x-shader/x-vertex"> // = object.matrixWorld uniform mat4 modelMatrix; // = camera.matrixWorldInverse * object.matrixWorld uniform mat4 modelViewMatrix; // = camera.projectionMatrix uniform mat4 projectionMatrix; // = camera.matrixWorldInverse uniform mat4 viewMatrix; // default vertex attributes provided by Geometry and BufferGeometry attribute vec3 position; //点在view坐标系下的坐标,将在光栅化部分插值赋给每一个fragment varying vec3 modelViewPosition; void main() { //计算点在view坐标系下的坐标 modelViewPosition=(viewMatrix*modelMatrix*vec4(position,1.0)).xyz; //计算标准坐标系坐标 gl_Position=projectionMatrix*viewMatrix*modelMatrix*vec4(position,1.0); }</script><script id="fragmentShader" type="x-shader/x-fragment"> precision highp float; // = camera.matrixWorldInverse uniform mat4 viewMatrix; //平面的法向量变换矩阵 uniform mat3 viewNormalMatrix; uniform vec4 plane;//[a,b,c,d],(a,b,c)is normal must be normalized, d is the constant varying vec3 modelViewPosition; //将传入的plane转换为view坐标系下的表示 vec4 planeToEC(vec4 plane,mat4 viewMatrix,mat3 viewNormalMatrix) { //(a,b,c)is normalized, a*a+b*b+c*c=1,(-ad,-bd,-cd) is on the plane ax+by+cz+d=0 vec3 normal=vec3(plane.x,plane.y,plane.z); //计算model坐标系下的r0 vec3 pointInPlaneWC=normal*-plane.w; //计算view坐标系下的r0 vec3 pointInPlaneEC=(viewMatrix*vec4(pointInPlaneWC.xyz,1.0)).xyz; //计算view坐标系下的法向量 vec3 normalOfPlaneInEC=normalize(viewNormalMatrix*normal); //返回view坐标系下的平面表示 //-dot(normalOfPlaneInEC,pointInPlaneEC)是根据view坐标系下的法向量和r0来计算d return vec4(normalOfPlaneInEC,-dot(normalOfPlaneInEC,pointInPlaneEC)); } //计算点到平面距离,此时平面和点都在view坐标系下 float distanceToPlane(vec4 plane,vec3 position) { //根据公式计算 float distance=dot(vec3(plane.x,plane.y,plane.z),position)+plane.w; return distance; } void main() { vec4 planeInEC=planeToEC(plane,viewMatrix,viewNormalMatrix); float distance=distanceToPlane(planeInEC,modelViewPosition); if(distance<0.0) //在平面法向量相反方向的fragment被丢弃,discard的fragment不影响color buffer也不影响depth buffer。如果不discard只是不赋值gl_FragColor的话,这些fragment只是默认黑色,还是会影响color buffer和depth buffer。因为它们总是更靠近视点,所以总是挡住内侧,你永远看不到一个球的内部。 discard; //gl_FrontFacing==true表示fragment来自图元正面 if(gl_FrontFacing==true&&distance>=0.0) //如果fragment来自正面,赋值蓝色 gl_FragColor=vec4(0.0,0.0,1.0,1.0); if(gl_FrontFacing==false&&distance>0.0) //负面,赋值绿色 gl_FragColor=vec4(0.0,1.0,0.0,1.0); }</script>
<script> if (!Detector.webgl) Detector.addGetWebGLMessage(); var container = document.getElementById( "container" ); var scene, renderer, camera; var mesh; renderer = new THREE.WebGLRenderer({antialias:true}); renderer.setPixelRatio( window.devicePixelRatio ); renderer.setSize( window.innerWidth, window.innerHeight ); container.appendChild(renderer.domElement); scene=new THREE.Scene(); var aspect = window.innerWidth / window.innerHeight; camera=new THREE.PerspectiveCamera(45,aspect,1,1000); camera.position.set(0,0,8); var controls=new THREE.OrbitControls(camera,renderer.domElement); controls.target.set(0,0,-0.0); controls.autoRotate=false; var rawShaderMaterial=new THREE.RawShaderMaterial( { uniforms: { plane:{value:new THREE.Vector4(0.0,0.0,-1.0,1.0)}, viewNormalMatrix:{value:new THREE.Matrix3()} }, vertexShader:document.getElementById('vertexShader').textContent, fragmentShader:document.getElementById('fragmentShader').textContent } ); //如果material.side的值为THREE.DoubleSide,render()函数里会调用gl.disable(gl.CULL_FACE),确保反面的fragment也会传给fragmentShader。 // rawShaderMaterial.side=THREE.DoubleSide; ballGeometry=new THREE.SphereGeometry(1.8,100,100); ballMaterial=rawShaderMaterial; ballMesh=new THREE.Mesh(ballGeometry,ballMaterial); scene.add(ballMesh); animate(); function animate() { requestAnimationFrame(animate); controls.update(); //获得平面的法向量转换矩阵 rawShaderMaterial.uniforms.viewNormalMatrix.value .getNormalMatrix(camera.matrixWorldInverse); renderer.render(scene,camera); }</script>
对于多个平面,比如说平面A,B,C,将算出距离da,db,dc。我们可以discard掉所有(da<0.0||db<0.0||dc<0.0)的fragment,即被任一个平面cull掉的fragment都不要。也可以只cull掉(da<0.0&&db<0.0&&dc<0.0)的fragment。
3.THREE.js的做法
THREE.js包装了webGL的做法。
//声明几个平面 var plane1=new THREE.Plane().setComponents(0,0,-1,1); var plane2=new THREE.Plane().setComponents(-1,0,0,0); var plane3=new THREE.Plane().setComponents(0,-1,0,0); //放到数组里 var clippingPlanes=[]; clippingPlanes.push(plane1); clippingPlanes.push(plane2); clippingPlanes.push(plane3); renderer = new THREE.WebGLRenderer({antialias:true}); renderer.setPixelRatio( window.devicePixelRatio ); renderer.setSize( window.innerWidth, window.innerHeight ); //数组赋值给WebGLRenderer的成员变量clippingPlanes,记得material.side=THREE.DoubleSide renderer.clippingPlanes=clippingPlanes;
而对于(da<0.0||db<0.0||dc<0.0)或(da<0.0&&db<0.0&&dc<0.0)的选取是设置material的clipIntersection属性。
.clipIntersection
Changes the behavior of clipping planes so that only their intersection is clipped, rather than their union. Default is false.
- webGL clipping plane和doubleside
- HTML5画布知识:在Three.js文件实现WebGL Plane
- ApiDemo 学习 CameraPreview 和 Clipping
- 视域控制——ArcGlobe中Clipping Plane Positions的作用
- Android 5.0学习之Tinting和Clipping
- 44-android5.0 Palette和Clipping
- Android 5.0学习之Tinting和Clipping
- Android 5.x的Tinting和Clipping
- unity 和webgl 互调传值
- 【WebGL】茶壶和光照
- PCB中plane和layer的区别
- PCB中plane和layer的区别
- h.264中的stride和plane
- PCB中plane和layer的区别
- x264 关于plane[0] 和 x264_picture_alloc
- PCB中Add layer 和 Plane 区别
- 使用Threejs和shaderMaterial给plane贴图
- android 图形图像编程- 第三章 路径(Path)和剪切(Clipping)
- vue对比其他框架
- 快速排序实现以及优化
- Linux新手笔记之Linux文件和目录
- serviced does not support chkconfig
- smtp发邮件
- webGL clipping plane和doubleside
- CCF 历年真题之打酱油(_1709_1_GetSomeSoySauce.java)参考答案
- java synchronized
- notepad++安装十六进制插件Hex Editer
- 打印99乘法表,并用continue剔除任意行
- 将博客搬至CSDN
- java web程序员应该懂多线程和jvm优化
- Qt中多模块的建立
- android AT指令获取SIM卡ICCID