OpenGL ES 2.0 中的剪裁平面

发布于 2024-12-04 23:28:06 字数 742 浏览 5 评论 0原文

我需要在 OpenGL ES 2.0 中的剪切平面下剪切数百个对象,并且希望了解对 OpenGL 的这个子集更有经验的人们的想法。

在 OpenGL ES 1.x 中有 glClipPlane。在桌面上,着色器中有 glClipPlane 或 gl_ClipDistance。这两者在 OpenGL ES 2.0 中都不可用。似乎这种功能在2.0中就完全消失了。

似乎唯一的方法是 A)在片段着色器中运行平面方程,或者 B)编写一个非常复杂的顶点着色器,将顶点放置在平面上(如果它们位于平面后面)。

(A) 与 glClipPlane 相比会很慢,因为“常规”裁剪是在顶点着色器之后和片段着色器之前完成的,每个片段仍然需要部分处理和丢弃。

(B) 很难在着色器之间兼容,因为我们不能丢弃顶点,我们必须将它们与平面对齐并调整“剪切”的属性。如果不发送纹理中的所有顶点并对其进行采样,则无法在着色器中的顶点之间进行插值,这将非常昂贵。通常,无论如何都可能无法正确插入数据。

我还考虑过将近平面与裁剪平面对齐,这将是一个有效的解决方案。

在渲染整个场景并检查深度失败后绘制平面也不起作用(除非您看起来接近垂直于平面)。

对于单个对象有效的方法是将平面绘制到深度缓冲区,然后使用 glDepthFunc(GL_GREATER) 渲染对象,但正如预期的那样,当一个对象位于另一个对象后面时,它不起作用。我尝试以这个概念为基础,但最终得到了与影子卷非常相似的东西,而且同样昂贵。

那么我错过了什么?如何在 OpenGL ES 2.0 中进行平面裁剪?

I need to clip a few hundred objects under a clipping plane in OpenGL ES 2.0 and would appreciate ideas from people more experienced with this subset of OpenGL.

In OpenGL ES 1.x there is glClipPlane. On the desktop you have glClipPlane, or gl_ClipDistance in your shader. Neither of these two are available in OpenGL ES 2.0. It seems that this kind of functionality disappeared completely with 2.0.

It seems the only way to do this is either A) run the plane equation in the fragment shader, or B) write a very complex vertex shader that positions vertices on the plane if they are behind it.

(A) would be slow compared to glClipPlane, since "regular" clipping is done after the vertex shader and before the fragment shader, each fragment would still have to be partially processed and discarded.

(B) would be very hard to make compatible between shaders, since we can't discard vertices we have to align them with the plane and adjust attributes for those that are "cut". It's not possible to interpolate between vertices in the shader without sending all vertices in a texture and sample it, which would be extremely expensive. Often it would probably be impossible to interpolate the data correcly anyway.

I've also thought of aligning the near plane with the clipping plane which would be an efficient solution.

And drawing a plane after rendering the whole scene and checking for depth-fail will not work either (unless you are looking close to perpendicular to the plane).

What works for a single object is to draw the plane to the depth buffer and then render the object with glDepthFunc(GL_GREATER), but as expected it does not work when one of the objects is behind another. I tried to build on to this concept but eventually ended up with something very similar to shadow volumes and just as expensive.

So what am I missing? How would you do plane clipping in OpenGL ES 2.0?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

绳情 2024-12-11 23:28:06

这是我在 Vuforia SDK 上找到的两个解决方案论坛

  1. 使用 Harri Smatt 的着色器:

    统一 mat4 uModelM;
    统一mat4 uViewProjectionM;
    属性 vec3 aPosition;
    改变 vec3 vPosition;
    无效主(){
      vec4 pos = uModelM * vec4(aPosition, 1.0);
      gl_Position = uViewProjectionM * pos;
      vPosition = pos.xyz / pos.w;
    }
    

    <小时>

    精度mediump浮点数;
    改变 vec3 vPosition;
    无效主(){
      if (vPosition.z < 0.0) {
        丢弃;
      } 别的 {
        // 选择实际颜色进行渲染..
      }
    }
    
  2. 在 Alessandro Boccalatte 的深度缓冲区中使用四边形:

    <块引用>

    • 禁用颜色写入(即设置glColorMask(false, false, false, false);
    • 渲染一个与标记形状匹配的四边形(即与标记大小和位置/方向相同的四边形);这只会渲染到深度缓冲区中(因为我们在上一步中禁用了颜色缓冲区写入)
    • 启用颜色遮罩 (glColorMask(true, true, true, true);)
    • 渲染 3D 模型

Here is two solutions I've found on Vuforia SDK forums.

  1. Using shaders by Harri Smatt:

    uniform mat4 uModelM;
    uniform mat4 uViewProjectionM;
    attribute vec3 aPosition;
    varying vec3 vPosition;
    void main() {
      vec4 pos = uModelM * vec4(aPosition, 1.0);
      gl_Position = uViewProjectionM * pos;
      vPosition = pos.xyz / pos.w;
    }
    

    precision mediump float;
    varying vec3 vPosition;
    void main() {
      if (vPosition.z < 0.0) {
        discard;
      } else {
        // Choose actual color for rendering..
      }
    }
    
  2. Using quad in depth buffer by Alessandro Boccalatte:

    • disable color writing (i.e. set the glColorMask(false, false, false, false);)
    • render a quad which matches the marker shape (i.e. just a quad with the same size and position/orientation of the marker); this will only be rendered into the depth buffer (because we disabled color buffer writing in the previous step)
    • enable back the color mask (glColorMask(true, true, true, true);)
    • render your 3D models
拥醉 2024-12-11 23:28:06

由于扩展 EXT_clip_cull_distance 在 OpenGL ES 2.0 中不可用(因为此扩展需要 OpenGL ES 3.0),因此必须模拟裁剪。它可以通过丢弃片段在片段着色器中进行模拟。请参阅片段着色器 - 特殊操作

另请参阅 OpenGL ES 着色语言 1.00 规范; 6.4 跳跃;第 58 页

discard 关键字仅允许在片段着色器中使用。它可以在片段着色器中使用以放弃对当前片段的操作。该关键字导致片段被丢弃,并且不会发生任何缓冲区的更新。它通常用在条件语句中,例如:

if(强度<0.0)
    丢弃;

模拟 gl_ClipDistance 的着色器程序可能如下所示:

顶点着色器:

attribute vec3 inPos;
attribute vec3 inCol;

varying vec3  vertCol;
varying float clip_distance;

uniform mat4 u_projectionMat44;
uniform mat4 u_viewMat44;
uniform mat4 u_modelMat44;
uniform vec4 u_clipPlane;

void main()
{   
    vertCol        = inCol;
    vec4 modelPos  = u_modelMat44 * vec4( inPos, 1.0 );
    gl_Position    = u_projectionMat44 * u_viewMat44 * viewPos;
    clip_distance  = dot(modelPos, u_clipPlane);
}

片段着色器:

varying vec3  vertPos;
varying vec3  vertCol;
varying float clip_distance;

void main()
{
    if ( clip_distance < 0.0 )
        discard;
    gl_FragColor = vec4( vertCol.rgb, 1.0 );
} 

以下 WebGL 示例演示了这一点。请注意,WebGL 1.0 上下文与 OpenGL ES 2.0 非常一致API。

var readInput = true;
  function changeEventHandler(event){
    readInput = true;
  }
  
  (function loadscene() {
  
  var gl, progDraw, vp_size;
  var bufCube = {};
  var clip = 0.0;
  
  function render(delteMS){

      if ( readInput ) {
          readInput = false;
          clip = (document.getElementById( "clip" ).value - 50) / 50;
      }

      Camera.create();
      Camera.vp = vp_size;
          
      gl.viewport( 0, 0, vp_size[0], vp_size[1] );
      gl.enable( gl.DEPTH_TEST );
      gl.clearColor( 0.0, 0.0, 0.0, 1.0 );
      gl.clear( gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT );

      // set up draw shader
      ShaderProgram.Use( progDraw );
      ShaderProgram.SetUniformM44( progDraw, "u_projectionMat44", Camera.Perspective() );
      ShaderProgram.SetUniformM44( progDraw, "u_viewMat44", Camera.LookAt() );
      var modelMat = IdentityMat44()
      modelMat = RotateAxis( modelMat, CalcAng( delteMS, 13.0 ), 0 );
      modelMat = RotateAxis( modelMat, CalcAng( delteMS, 17.0 ), 1 );
      ShaderProgram.SetUniformM44( progDraw, "u_modelMat44", modelMat );
      ShaderProgram.SetUniformF4( progDraw, "u_clipPlane", [1.0,-1.0,0.0,clip*1.7321] );
      
      // draw scene
      VertexBuffer.Draw( bufCube );

      requestAnimationFrame(render);
  }
  
  function resize() {
      //vp_size = [gl.drawingBufferWidth, gl.drawingBufferHeight];
      vp_size = [window.innerWidth, window.innerHeight]
      canvas.width = vp_size[0];
      canvas.height = vp_size[1];
  }
  
  function initScene() {
  
      canvas = document.getElementById( "canvas");
      gl = canvas.getContext( "experimental-webgl" );
      //gl = canvas.getContext( "webgl2" );
      if ( !gl )
        return null;
      
      /*
      var ext_frag_depth = gl.getExtension( "EXT_clip_cull_distance" );  // gl_ClipDistance gl_CullDistance
      if (!ext_frag_depth)
          alert('no gl_ClipDistance and gl_CullDistance support');
      */

      progDraw = ShaderProgram.Create( 
        [ { source : "draw-shader-vs", stage : gl.VERTEX_SHADER },
          { source : "draw-shader-fs", stage : gl.FRAGMENT_SHADER }
        ] );
      if ( !progDraw.progObj )
          return null;
      progDraw.inPos = ShaderProgram.AttributeIndex( progDraw, "inPos" );
      progDraw.inNV  = ShaderProgram.AttributeIndex( progDraw, "inNV" );
      progDraw.inCol = ShaderProgram.AttributeIndex( progDraw, "inCol" );
      
      // create cube
      var cubePos = [
        -1.0, -1.0,  1.0,  1.0, -1.0,  1.0,  1.0,  1.0,  1.0, -1.0,  1.0,  1.0,
        -1.0, -1.0, -1.0,  1.0, -1.0, -1.0,  1.0,  1.0, -1.0, -1.0,  1.0, -1.0 ];
      var cubeCol = [ 1.0, 0.0, 0.0, 1.0, 0.5, 0.0, 1.0, 0.0, 1.0, 1.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0 ];
      var cubeHlpInx = [ 0, 1, 2, 3, 1, 5, 6, 2, 5, 4, 7, 6, 4, 0, 3, 7, 3, 2, 6, 7, 1, 0, 4, 5 ];  
      var cubePosData = [];
      for ( var i = 0; i < cubeHlpInx.length; ++ i ) {
        cubePosData.push( cubePos[cubeHlpInx[i]*3], cubePos[cubeHlpInx[i]*3+1], cubePos[cubeHlpInx[i]*3+2] );
      }
      var cubeNVData = [];
      for ( var i1 = 0; i1 < cubeHlpInx.length; i1 += 4 ) {
      var nv = [0, 0, 0];
      for ( i2 = 0; i2 < 4; ++ i2 ) {
          var i = i1 + i2;
          nv[0] += cubePosData[i*3]; nv[1] += cubePosData[i*3+1]; nv[2] += cubePosData[i*3+2];
      }
      for ( i2 = 0; i2 < 4; ++ i2 )
        cubeNVData.push( nv[0], nv[1], nv[2] );
      }
      var cubeColData = [];
      for ( var is = 0; is < 6; ++ is ) {
        for ( var ip = 0; ip < 4; ++ ip ) {
         cubeColData.push( cubeCol[is*3], cubeCol[is*3+1], cubeCol[is*3+2] ); 
        }
      }
      var cubeInxData = [];
      for ( var i = 0; i < cubeHlpInx.length; i += 4 ) {
        cubeInxData.push( i, i+1, i+2, i, i+2, i+3 );   
      }
      bufCube = VertexBuffer.Create(
      [ { data : cubePosData, attrSize : 3, attrLoc : progDraw.inPos },
        { data : cubeNVData,  attrSize : 3, attrLoc : progDraw.inNV },
        { data : cubeColData, attrSize : 3, attrLoc : progDraw.inCol } ],
        cubeInxData );
        
      window.onresize = resize;
      resize();
      requestAnimationFrame(render);
  }
  
  function Fract( val ) { 
      return val - Math.trunc( val );
  }
  function CalcAng( deltaTime, intervall ) {
      return Fract( deltaTime / (1000*intervall) ) * 2.0 * Math.PI;
  }
  function CalcMove( deltaTime, intervall, range ) {
      var pos = self.Fract( deltaTime / (1000*intervall) ) * 2.0
      var pos = pos < 1.0 ? pos : (2.0-pos)
      return range[0] + (range[1] - range[0]) * pos;
  }    
  function EllipticalPosition( a, b, angRag ) {
      var a_b = a * a - b * b
      var ea = (a_b <= 0) ? 0 : Math.sqrt( a_b );
      var eb = (a_b >= 0) ? 0 : Math.sqrt( -a_b );
      return [ a * Math.sin( angRag ) - ea, b * Math.cos( angRag ) - eb, 0 ];
  }
  
  glArrayType = typeof Float32Array !="undefined" ? Float32Array : ( typeof WebGLFloatArray != "undefined" ? WebGLFloatArray : Array );
  
  function IdentityMat44() {
    var m = new glArrayType(16);
    m[0]  = 1; m[1]  = 0; m[2]  = 0; m[3]  = 0;
    m[4]  = 0; m[5]  = 1; m[6]  = 0; m[7]  = 0;
    m[8]  = 0; m[9]  = 0; m[10] = 1; m[11] = 0;
    m[12] = 0; m[13] = 0; m[14] = 0; m[15] = 1;
    return m;
  };
  
  function RotateAxis(matA, angRad, axis) {
      var aMap = [ [1, 2], [2, 0], [0, 1] ];
      var a0 = aMap[axis][0], a1 = aMap[axis][1]; 
      var sinAng = Math.sin(angRad), cosAng = Math.cos(angRad);
      var matB = new glArrayType(16);
      for ( var i = 0; i < 16; ++ i ) matB[i] = matA[i];
      for ( var i = 0; i < 3; ++ i ) {
          matB[a0*4+i] = matA[a0*4+i] * cosAng + matA[a1*4+i] * sinAng;
          matB[a1*4+i] = matA[a0*4+i] * -sinAng + matA[a1*4+i] * cosAng;
      }
      return matB;
  }
  
  function Cross( a, b ) { return [ a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0], 0.0 ]; }
  function Dot( a, b ) { return a[0]*b[0] + a[1]*b[1] + a[2]*b[2]; }
  function Normalize( v ) {
      var len = Math.sqrt( v[0] * v[0] + v[1] * v[1] + v[2] * v[2] );
      return [ v[0] / len, v[1] / len, v[2] / len ];
  }
  
  var Camera = {};
  Camera.create = function() {
      this.pos    = [0, 3, 0.0];
      this.target = [0, 0, 0];
      this.up     = [0, 0, 1];
      this.fov_y  = 90;
      this.vp     = [800, 600];
      this.near   = 0.5;
      this.far    = 100.0;
  }
  Camera.Perspective = function() {
      var fn = this.far + this.near;
      var f_n = this.far - this.near;
      var r = this.vp[0] / this.vp[1];
      var t = 1 / Math.tan( Math.PI * this.fov_y / 360 );
      var m = IdentityMat44();
      m[0]  = t/r; m[1]  = 0; m[2]  =  0;                              m[3]  = 0;
      m[4]  = 0;   m[5]  = t; m[6]  =  0;                              m[7]  = 0;
      m[8]  = 0;   m[9]  = 0; m[10] = -fn / f_n;                       m[11] = -1;
      m[12] = 0;   m[13] = 0; m[14] = -2 * this.far * this.near / f_n; m[15] =  0;
      return m;
  }
  Camera.LookAt = function() {
      var mz = Normalize( [ this.pos[0]-this.target[0], this.pos[1]-this.target[1], this.pos[2]-this.target[2] ] );
      var mx = Normalize( Cross( this.up, mz ) );
      var my = Normalize( Cross( mz, mx ) );
      var tx = Dot( mx, this.pos );
      var ty = Dot( my, this.pos );
      var tz = Dot( [-mz[0], -mz[1], -mz[2]], this.pos ); 
      var m = IdentityMat44();
      m[0]  = mx[0]; m[1]  = my[0]; m[2]  = mz[0]; m[3]  = 0;
      m[4]  = mx[1]; m[5]  = my[1]; m[6]  = mz[1]; m[7]  = 0;
      m[8]  = mx[2]; m[9]  = my[2]; m[10] = mz[2]; m[11] = 0;
      m[12] = tx;    m[13] = ty;    m[14] = tz;    m[15] = 1; 
      return m;
  } 
  
  var ShaderProgram = {};
  ShaderProgram.Create = function( shaderList ) {
      var shaderObjs = [];
      for ( var i_sh = 0; i_sh < shaderList.length; ++ i_sh ) {
          var shderObj = this.CompileShader( shaderList[i_sh].source, shaderList[i_sh].stage );
          if ( shderObj == 0 )
              return 0;
          shaderObjs.push( shderObj );
      }
      var prog = {}
      prog.progObj = this.LinkProgram( shaderObjs )
      if ( prog.progObj ) {
          prog.attribIndex = {};
          var noOfAttributes = gl.getProgramParameter( prog.progObj, gl.ACTIVE_ATTRIBUTES );
          for ( var i_n = 0; i_n < noOfAttributes; ++ i_n ) {
              var name = gl.getActiveAttrib( prog.progObj, i_n ).name;
              prog.attribIndex[name] = gl.getAttribLocation( prog.progObj, name );
          }
          prog.unifomLocation = {};
          var noOfUniforms = gl.getProgramParameter( prog.progObj, gl.ACTIVE_UNIFORMS );
          for ( var i_n = 0; i_n < noOfUniforms; ++ i_n ) {
              var name = gl.getActiveUniform( prog.progObj, i_n ).name;
              prog.unifomLocation[name] = gl.getUniformLocation( prog.progObj, name );
          }
      }
      return prog;
  }
  ShaderProgram.AttributeIndex = function( prog, name ) { return prog.attribIndex[name]; } 
  ShaderProgram.UniformLocation = function( prog, name ) { return prog.unifomLocation[name]; } 
  ShaderProgram.Use = function( prog ) { gl.useProgram( prog.progObj ); } 
  ShaderProgram.SetUniformI1  = function( prog, name, val ) { if(prog.unifomLocation[name]) gl.uniform1i( prog.unifomLocation[name], val ); }
  ShaderProgram.SetUniformF1  = function( prog, name, val ) { if(prog.unifomLocation[name]) gl.uniform1f( prog.unifomLocation[name], val ); }
  ShaderProgram.SetUniformF2  = function( prog, name, arr ) { if(prog.unifomLocation[name]) gl.uniform2fv( prog.unifomLocation[name], arr ); }
  ShaderProgram.SetUniformF3  = function( prog, name, arr ) { if(prog.unifomLocation[name]) gl.uniform3fv( prog.unifomLocation[name], arr ); }
  ShaderProgram.SetUniformF4  = function( prog, name, arr ) { if(prog.unifomLocation[name]) gl.uniform4fv( prog.unifomLocation[name], arr ); }
  ShaderProgram.SetUniformM33 = function( prog, name, mat ) { if(prog.unifomLocation[name]) gl.uniformMatrix3fv( prog.unifomLocation[name], false, mat ); }
  ShaderProgram.SetUniformM44 = function( prog, name, mat ) { if(prog.unifomLocation[name]) gl.uniformMatrix4fv( prog.unifomLocation[name], false, mat ); }
  ShaderProgram.CompileShader = function( source, shaderStage ) {
      var shaderScript = document.getElementById(source);
      if (shaderScript)
        source = shaderScript.text;
      var shaderObj = gl.createShader( shaderStage );
      gl.shaderSource( shaderObj, source );
      gl.compileShader( shaderObj );
      var status = gl.getShaderParameter( shaderObj, gl.COMPILE_STATUS );
      if ( !status ) alert(gl.getShaderInfoLog(shaderObj));
      return status ? shaderObj : null;
  } 
  ShaderProgram.LinkProgram = function( shaderObjs ) {
      var prog = gl.createProgram();
      for ( var i_sh = 0; i_sh < shaderObjs.length; ++ i_sh )
          gl.attachShader( prog, shaderObjs[i_sh] );
      gl.linkProgram( prog );
      status = gl.getProgramParameter( prog, gl.LINK_STATUS );
      if ( !status ) alert("Could not initialise shaders");
      gl.useProgram( null );
      return status ? prog : null;
  }
  
  var VertexBuffer = {};
  VertexBuffer.Create = function( attributes, indices ) {
      var buffer = {};
      buffer.buf = [];
      buffer.attr = []
      for ( var i = 0; i < attributes.length; ++ i ) {
          buffer.buf.push( gl.createBuffer() );
          buffer.attr.push( { size : attributes[i].attrSize, loc : attributes[i].attrLoc } );
          gl.bindBuffer( gl.ARRAY_BUFFER, buffer.buf[i] );
          gl.bufferData( gl.ARRAY_BUFFER, new Float32Array( attributes[i].data ), gl.STATIC_DRAW );
      }
      buffer.inx = gl.createBuffer();
      gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, buffer.inx );
      gl.bufferData( gl.ELEMENT_ARRAY_BUFFER, new Uint16Array( indices ), gl.STATIC_DRAW );
      buffer.inxLen = indices.length;
      gl.bindBuffer( gl.ARRAY_BUFFER, null );
      gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, null );
      return buffer;
  }
  VertexBuffer.Draw = function( bufObj ) {
    for ( var i = 0; i < bufObj.buf.length; ++ i ) {
          gl.bindBuffer( gl.ARRAY_BUFFER, bufObj.buf[i] );
          gl.vertexAttribPointer( bufObj.attr[i].loc, bufObj.attr[i].size, gl.FLOAT, false, 0, 0 );
          gl.enableVertexAttribArray( bufObj.attr[i].loc );
      }
      gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, bufObj.inx );
      gl.drawElements( gl.TRIANGLES, bufObj.inxLen, gl.UNSIGNED_SHORT, 0 );
      for ( var i = 0; i < bufObj.buf.length; ++ i )
         gl.disableVertexAttribArray( bufObj.attr[i].loc );
      gl.bindBuffer( gl.ARRAY_BUFFER, null );
      gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, null );
  }
  
  initScene();
  
  })();
html,body {
    height: 100%;
    width: 100%;
    margin: 0;
    overflow: hidden;
}

#gui {
    position : absolute;
    top : 0;
    left : 0;
}
<script id="draw-shader-vs" type="x-shader/x-vertex">
    precision highp float;
    
    attribute vec3 inPos;
    attribute vec3 inNV;
    attribute vec3 inCol;
  
    varying vec3  vertPos;
    varying vec3  vertNV;
    varying vec3  vertCol;
    varying float clip_distance;
    
    uniform mat4 u_projectionMat44;
    uniform mat4 u_viewMat44;
    uniform mat4 u_modelMat44;
    uniform vec4 u_clipPlane;
    
    void main()
    {   
        mat4 mv       = u_viewMat44 * u_modelMat44; 
        vertCol       = inCol;
        vertNV        = normalize(mat3(mv) * inNV);
        vec4 viewPos  = mv * vec4( inPos, 1.0 );
        vertPos       = viewPos.xyz;
        gl_Position   = u_projectionMat44 * viewPos;

        vec4 modelPos  = u_modelMat44 * vec4( inPos, 1.0 );
        vec4 clipPlane = vec4(normalize(u_clipPlane.xyz), u_clipPlane.w); 
        clip_distance  = dot(modelPos, clipPlane);
    }
</script>
  
<script id="draw-shader-fs" type="x-shader/x-fragment">
    precision mediump float;

    varying vec3  vertPos;
    varying vec3  vertNV;
    varying vec3  vertCol;
    varying float clip_distance;
    
    void main()
    {
        if ( clip_distance < 0.0 )
            discard;
        vec3 color   = vertCol;
        gl_FragColor = vec4( color.rgb, 1.0 );
    } 
</script>

<div>
    <form id="gui" name="inputs">
        <table>
            <tr> <td> <font color= #CCF>clipping</font> </td> 
                 <td> <input type="range" id="clip" min="0" max="100" value="50" onchange="changeEventHandler(event);"/></td> </tr>
        </table>
    </form>
</div>


<canvas id="canvas" style="border: none;" width="100%" height="100%"></canvas>

Since the extension EXT_clip_cull_distance is not available in OpenGL ES 2.0 (because for this extension is OpenGL ES 3.0 is required), clipping has to be emulated. It can be emulated in the fragment shader, by discarding fragments. See Fragment Shader - Special operations.

See also OpenGL ES Shading Language 1.00 Specification; 6.4 Jumps; page 58:

The discard keyword is only allowed within fragment shaders. It can be used within a fragment shader to abandon the operation on the current fragment. This keyword causes the fragment to be discarded and no updates to any buffers will occur. It would typically be used within a conditional statement, for example:

if (intensity < 0.0)
    discard;

A shader program which emulates gl_ClipDistance may look like this:

Vertex shader:

attribute vec3 inPos;
attribute vec3 inCol;

varying vec3  vertCol;
varying float clip_distance;

uniform mat4 u_projectionMat44;
uniform mat4 u_viewMat44;
uniform mat4 u_modelMat44;
uniform vec4 u_clipPlane;

void main()
{   
    vertCol        = inCol;
    vec4 modelPos  = u_modelMat44 * vec4( inPos, 1.0 );
    gl_Position    = u_projectionMat44 * u_viewMat44 * viewPos;
    clip_distance  = dot(modelPos, u_clipPlane);
}

Fragment shader:

varying vec3  vertPos;
varying vec3  vertCol;
varying float clip_distance;

void main()
{
    if ( clip_distance < 0.0 )
        discard;
    gl_FragColor = vec4( vertCol.rgb, 1.0 );
} 

The following WebGL example demonstrates this. Note, the WebGL 1.0 context conforms closely to the OpenGL ES 2.0 API.

var readInput = true;
  function changeEventHandler(event){
    readInput = true;
  }
  
  (function loadscene() {
  
  var gl, progDraw, vp_size;
  var bufCube = {};
  var clip = 0.0;
  
  function render(delteMS){

      if ( readInput ) {
          readInput = false;
          clip = (document.getElementById( "clip" ).value - 50) / 50;
      }

      Camera.create();
      Camera.vp = vp_size;
          
      gl.viewport( 0, 0, vp_size[0], vp_size[1] );
      gl.enable( gl.DEPTH_TEST );
      gl.clearColor( 0.0, 0.0, 0.0, 1.0 );
      gl.clear( gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT );

      // set up draw shader
      ShaderProgram.Use( progDraw );
      ShaderProgram.SetUniformM44( progDraw, "u_projectionMat44", Camera.Perspective() );
      ShaderProgram.SetUniformM44( progDraw, "u_viewMat44", Camera.LookAt() );
      var modelMat = IdentityMat44()
      modelMat = RotateAxis( modelMat, CalcAng( delteMS, 13.0 ), 0 );
      modelMat = RotateAxis( modelMat, CalcAng( delteMS, 17.0 ), 1 );
      ShaderProgram.SetUniformM44( progDraw, "u_modelMat44", modelMat );
      ShaderProgram.SetUniformF4( progDraw, "u_clipPlane", [1.0,-1.0,0.0,clip*1.7321] );
      
      // draw scene
      VertexBuffer.Draw( bufCube );

      requestAnimationFrame(render);
  }
  
  function resize() {
      //vp_size = [gl.drawingBufferWidth, gl.drawingBufferHeight];
      vp_size = [window.innerWidth, window.innerHeight]
      canvas.width = vp_size[0];
      canvas.height = vp_size[1];
  }
  
  function initScene() {
  
      canvas = document.getElementById( "canvas");
      gl = canvas.getContext( "experimental-webgl" );
      //gl = canvas.getContext( "webgl2" );
      if ( !gl )
        return null;
      
      /*
      var ext_frag_depth = gl.getExtension( "EXT_clip_cull_distance" );  // gl_ClipDistance gl_CullDistance
      if (!ext_frag_depth)
          alert('no gl_ClipDistance and gl_CullDistance support');
      */

      progDraw = ShaderProgram.Create( 
        [ { source : "draw-shader-vs", stage : gl.VERTEX_SHADER },
          { source : "draw-shader-fs", stage : gl.FRAGMENT_SHADER }
        ] );
      if ( !progDraw.progObj )
          return null;
      progDraw.inPos = ShaderProgram.AttributeIndex( progDraw, "inPos" );
      progDraw.inNV  = ShaderProgram.AttributeIndex( progDraw, "inNV" );
      progDraw.inCol = ShaderProgram.AttributeIndex( progDraw, "inCol" );
      
      // create cube
      var cubePos = [
        -1.0, -1.0,  1.0,  1.0, -1.0,  1.0,  1.0,  1.0,  1.0, -1.0,  1.0,  1.0,
        -1.0, -1.0, -1.0,  1.0, -1.0, -1.0,  1.0,  1.0, -1.0, -1.0,  1.0, -1.0 ];
      var cubeCol = [ 1.0, 0.0, 0.0, 1.0, 0.5, 0.0, 1.0, 0.0, 1.0, 1.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0 ];
      var cubeHlpInx = [ 0, 1, 2, 3, 1, 5, 6, 2, 5, 4, 7, 6, 4, 0, 3, 7, 3, 2, 6, 7, 1, 0, 4, 5 ];  
      var cubePosData = [];
      for ( var i = 0; i < cubeHlpInx.length; ++ i ) {
        cubePosData.push( cubePos[cubeHlpInx[i]*3], cubePos[cubeHlpInx[i]*3+1], cubePos[cubeHlpInx[i]*3+2] );
      }
      var cubeNVData = [];
      for ( var i1 = 0; i1 < cubeHlpInx.length; i1 += 4 ) {
      var nv = [0, 0, 0];
      for ( i2 = 0; i2 < 4; ++ i2 ) {
          var i = i1 + i2;
          nv[0] += cubePosData[i*3]; nv[1] += cubePosData[i*3+1]; nv[2] += cubePosData[i*3+2];
      }
      for ( i2 = 0; i2 < 4; ++ i2 )
        cubeNVData.push( nv[0], nv[1], nv[2] );
      }
      var cubeColData = [];
      for ( var is = 0; is < 6; ++ is ) {
        for ( var ip = 0; ip < 4; ++ ip ) {
         cubeColData.push( cubeCol[is*3], cubeCol[is*3+1], cubeCol[is*3+2] ); 
        }
      }
      var cubeInxData = [];
      for ( var i = 0; i < cubeHlpInx.length; i += 4 ) {
        cubeInxData.push( i, i+1, i+2, i, i+2, i+3 );   
      }
      bufCube = VertexBuffer.Create(
      [ { data : cubePosData, attrSize : 3, attrLoc : progDraw.inPos },
        { data : cubeNVData,  attrSize : 3, attrLoc : progDraw.inNV },
        { data : cubeColData, attrSize : 3, attrLoc : progDraw.inCol } ],
        cubeInxData );
        
      window.onresize = resize;
      resize();
      requestAnimationFrame(render);
  }
  
  function Fract( val ) { 
      return val - Math.trunc( val );
  }
  function CalcAng( deltaTime, intervall ) {
      return Fract( deltaTime / (1000*intervall) ) * 2.0 * Math.PI;
  }
  function CalcMove( deltaTime, intervall, range ) {
      var pos = self.Fract( deltaTime / (1000*intervall) ) * 2.0
      var pos = pos < 1.0 ? pos : (2.0-pos)
      return range[0] + (range[1] - range[0]) * pos;
  }    
  function EllipticalPosition( a, b, angRag ) {
      var a_b = a * a - b * b
      var ea = (a_b <= 0) ? 0 : Math.sqrt( a_b );
      var eb = (a_b >= 0) ? 0 : Math.sqrt( -a_b );
      return [ a * Math.sin( angRag ) - ea, b * Math.cos( angRag ) - eb, 0 ];
  }
  
  glArrayType = typeof Float32Array !="undefined" ? Float32Array : ( typeof WebGLFloatArray != "undefined" ? WebGLFloatArray : Array );
  
  function IdentityMat44() {
    var m = new glArrayType(16);
    m[0]  = 1; m[1]  = 0; m[2]  = 0; m[3]  = 0;
    m[4]  = 0; m[5]  = 1; m[6]  = 0; m[7]  = 0;
    m[8]  = 0; m[9]  = 0; m[10] = 1; m[11] = 0;
    m[12] = 0; m[13] = 0; m[14] = 0; m[15] = 1;
    return m;
  };
  
  function RotateAxis(matA, angRad, axis) {
      var aMap = [ [1, 2], [2, 0], [0, 1] ];
      var a0 = aMap[axis][0], a1 = aMap[axis][1]; 
      var sinAng = Math.sin(angRad), cosAng = Math.cos(angRad);
      var matB = new glArrayType(16);
      for ( var i = 0; i < 16; ++ i ) matB[i] = matA[i];
      for ( var i = 0; i < 3; ++ i ) {
          matB[a0*4+i] = matA[a0*4+i] * cosAng + matA[a1*4+i] * sinAng;
          matB[a1*4+i] = matA[a0*4+i] * -sinAng + matA[a1*4+i] * cosAng;
      }
      return matB;
  }
  
  function Cross( a, b ) { return [ a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0], 0.0 ]; }
  function Dot( a, b ) { return a[0]*b[0] + a[1]*b[1] + a[2]*b[2]; }
  function Normalize( v ) {
      var len = Math.sqrt( v[0] * v[0] + v[1] * v[1] + v[2] * v[2] );
      return [ v[0] / len, v[1] / len, v[2] / len ];
  }
  
  var Camera = {};
  Camera.create = function() {
      this.pos    = [0, 3, 0.0];
      this.target = [0, 0, 0];
      this.up     = [0, 0, 1];
      this.fov_y  = 90;
      this.vp     = [800, 600];
      this.near   = 0.5;
      this.far    = 100.0;
  }
  Camera.Perspective = function() {
      var fn = this.far + this.near;
      var f_n = this.far - this.near;
      var r = this.vp[0] / this.vp[1];
      var t = 1 / Math.tan( Math.PI * this.fov_y / 360 );
      var m = IdentityMat44();
      m[0]  = t/r; m[1]  = 0; m[2]  =  0;                              m[3]  = 0;
      m[4]  = 0;   m[5]  = t; m[6]  =  0;                              m[7]  = 0;
      m[8]  = 0;   m[9]  = 0; m[10] = -fn / f_n;                       m[11] = -1;
      m[12] = 0;   m[13] = 0; m[14] = -2 * this.far * this.near / f_n; m[15] =  0;
      return m;
  }
  Camera.LookAt = function() {
      var mz = Normalize( [ this.pos[0]-this.target[0], this.pos[1]-this.target[1], this.pos[2]-this.target[2] ] );
      var mx = Normalize( Cross( this.up, mz ) );
      var my = Normalize( Cross( mz, mx ) );
      var tx = Dot( mx, this.pos );
      var ty = Dot( my, this.pos );
      var tz = Dot( [-mz[0], -mz[1], -mz[2]], this.pos ); 
      var m = IdentityMat44();
      m[0]  = mx[0]; m[1]  = my[0]; m[2]  = mz[0]; m[3]  = 0;
      m[4]  = mx[1]; m[5]  = my[1]; m[6]  = mz[1]; m[7]  = 0;
      m[8]  = mx[2]; m[9]  = my[2]; m[10] = mz[2]; m[11] = 0;
      m[12] = tx;    m[13] = ty;    m[14] = tz;    m[15] = 1; 
      return m;
  } 
  
  var ShaderProgram = {};
  ShaderProgram.Create = function( shaderList ) {
      var shaderObjs = [];
      for ( var i_sh = 0; i_sh < shaderList.length; ++ i_sh ) {
          var shderObj = this.CompileShader( shaderList[i_sh].source, shaderList[i_sh].stage );
          if ( shderObj == 0 )
              return 0;
          shaderObjs.push( shderObj );
      }
      var prog = {}
      prog.progObj = this.LinkProgram( shaderObjs )
      if ( prog.progObj ) {
          prog.attribIndex = {};
          var noOfAttributes = gl.getProgramParameter( prog.progObj, gl.ACTIVE_ATTRIBUTES );
          for ( var i_n = 0; i_n < noOfAttributes; ++ i_n ) {
              var name = gl.getActiveAttrib( prog.progObj, i_n ).name;
              prog.attribIndex[name] = gl.getAttribLocation( prog.progObj, name );
          }
          prog.unifomLocation = {};
          var noOfUniforms = gl.getProgramParameter( prog.progObj, gl.ACTIVE_UNIFORMS );
          for ( var i_n = 0; i_n < noOfUniforms; ++ i_n ) {
              var name = gl.getActiveUniform( prog.progObj, i_n ).name;
              prog.unifomLocation[name] = gl.getUniformLocation( prog.progObj, name );
          }
      }
      return prog;
  }
  ShaderProgram.AttributeIndex = function( prog, name ) { return prog.attribIndex[name]; } 
  ShaderProgram.UniformLocation = function( prog, name ) { return prog.unifomLocation[name]; } 
  ShaderProgram.Use = function( prog ) { gl.useProgram( prog.progObj ); } 
  ShaderProgram.SetUniformI1  = function( prog, name, val ) { if(prog.unifomLocation[name]) gl.uniform1i( prog.unifomLocation[name], val ); }
  ShaderProgram.SetUniformF1  = function( prog, name, val ) { if(prog.unifomLocation[name]) gl.uniform1f( prog.unifomLocation[name], val ); }
  ShaderProgram.SetUniformF2  = function( prog, name, arr ) { if(prog.unifomLocation[name]) gl.uniform2fv( prog.unifomLocation[name], arr ); }
  ShaderProgram.SetUniformF3  = function( prog, name, arr ) { if(prog.unifomLocation[name]) gl.uniform3fv( prog.unifomLocation[name], arr ); }
  ShaderProgram.SetUniformF4  = function( prog, name, arr ) { if(prog.unifomLocation[name]) gl.uniform4fv( prog.unifomLocation[name], arr ); }
  ShaderProgram.SetUniformM33 = function( prog, name, mat ) { if(prog.unifomLocation[name]) gl.uniformMatrix3fv( prog.unifomLocation[name], false, mat ); }
  ShaderProgram.SetUniformM44 = function( prog, name, mat ) { if(prog.unifomLocation[name]) gl.uniformMatrix4fv( prog.unifomLocation[name], false, mat ); }
  ShaderProgram.CompileShader = function( source, shaderStage ) {
      var shaderScript = document.getElementById(source);
      if (shaderScript)
        source = shaderScript.text;
      var shaderObj = gl.createShader( shaderStage );
      gl.shaderSource( shaderObj, source );
      gl.compileShader( shaderObj );
      var status = gl.getShaderParameter( shaderObj, gl.COMPILE_STATUS );
      if ( !status ) alert(gl.getShaderInfoLog(shaderObj));
      return status ? shaderObj : null;
  } 
  ShaderProgram.LinkProgram = function( shaderObjs ) {
      var prog = gl.createProgram();
      for ( var i_sh = 0; i_sh < shaderObjs.length; ++ i_sh )
          gl.attachShader( prog, shaderObjs[i_sh] );
      gl.linkProgram( prog );
      status = gl.getProgramParameter( prog, gl.LINK_STATUS );
      if ( !status ) alert("Could not initialise shaders");
      gl.useProgram( null );
      return status ? prog : null;
  }
  
  var VertexBuffer = {};
  VertexBuffer.Create = function( attributes, indices ) {
      var buffer = {};
      buffer.buf = [];
      buffer.attr = []
      for ( var i = 0; i < attributes.length; ++ i ) {
          buffer.buf.push( gl.createBuffer() );
          buffer.attr.push( { size : attributes[i].attrSize, loc : attributes[i].attrLoc } );
          gl.bindBuffer( gl.ARRAY_BUFFER, buffer.buf[i] );
          gl.bufferData( gl.ARRAY_BUFFER, new Float32Array( attributes[i].data ), gl.STATIC_DRAW );
      }
      buffer.inx = gl.createBuffer();
      gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, buffer.inx );
      gl.bufferData( gl.ELEMENT_ARRAY_BUFFER, new Uint16Array( indices ), gl.STATIC_DRAW );
      buffer.inxLen = indices.length;
      gl.bindBuffer( gl.ARRAY_BUFFER, null );
      gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, null );
      return buffer;
  }
  VertexBuffer.Draw = function( bufObj ) {
    for ( var i = 0; i < bufObj.buf.length; ++ i ) {
          gl.bindBuffer( gl.ARRAY_BUFFER, bufObj.buf[i] );
          gl.vertexAttribPointer( bufObj.attr[i].loc, bufObj.attr[i].size, gl.FLOAT, false, 0, 0 );
          gl.enableVertexAttribArray( bufObj.attr[i].loc );
      }
      gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, bufObj.inx );
      gl.drawElements( gl.TRIANGLES, bufObj.inxLen, gl.UNSIGNED_SHORT, 0 );
      for ( var i = 0; i < bufObj.buf.length; ++ i )
         gl.disableVertexAttribArray( bufObj.attr[i].loc );
      gl.bindBuffer( gl.ARRAY_BUFFER, null );
      gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, null );
  }
  
  initScene();
  
  })();
html,body {
    height: 100%;
    width: 100%;
    margin: 0;
    overflow: hidden;
}

#gui {
    position : absolute;
    top : 0;
    left : 0;
}
<script id="draw-shader-vs" type="x-shader/x-vertex">
    precision highp float;
    
    attribute vec3 inPos;
    attribute vec3 inNV;
    attribute vec3 inCol;
  
    varying vec3  vertPos;
    varying vec3  vertNV;
    varying vec3  vertCol;
    varying float clip_distance;
    
    uniform mat4 u_projectionMat44;
    uniform mat4 u_viewMat44;
    uniform mat4 u_modelMat44;
    uniform vec4 u_clipPlane;
    
    void main()
    {   
        mat4 mv       = u_viewMat44 * u_modelMat44; 
        vertCol       = inCol;
        vertNV        = normalize(mat3(mv) * inNV);
        vec4 viewPos  = mv * vec4( inPos, 1.0 );
        vertPos       = viewPos.xyz;
        gl_Position   = u_projectionMat44 * viewPos;

        vec4 modelPos  = u_modelMat44 * vec4( inPos, 1.0 );
        vec4 clipPlane = vec4(normalize(u_clipPlane.xyz), u_clipPlane.w); 
        clip_distance  = dot(modelPos, clipPlane);
    }
</script>
  
<script id="draw-shader-fs" type="x-shader/x-fragment">
    precision mediump float;

    varying vec3  vertPos;
    varying vec3  vertNV;
    varying vec3  vertCol;
    varying float clip_distance;
    
    void main()
    {
        if ( clip_distance < 0.0 )
            discard;
        vec3 color   = vertCol;
        gl_FragColor = vec4( color.rgb, 1.0 );
    } 
</script>

<div>
    <form id="gui" name="inputs">
        <table>
            <tr> <td> <font color= #CCF>clipping</font> </td> 
                 <td> <input type="range" id="clip" min="0" max="100" value="50" onchange="changeEventHandler(event);"/></td> </tr>
        </table>
    </form>
</div>


<canvas id="canvas" style="border: none;" width="100%" height="100%"></canvas>

烧了回忆取暖 2024-12-11 23:28:06

我不知道这是否适用于 OpenGL ES,但 OpenGL 具有由 glEnable(GL_CLIP_DISTANCE0) 启用的 gl_ClipDistance 变化输出。启用后,图元将被剪裁,使得在顶点和几何着色器之后 gl_ClipDistance[0] >= 0。

剪辑距离可以指定为与世界空间平面方程的点积:

http://github .prideout.net/clip-planes/

I don't know if this applies to OpenGL ES, but OpenGL has the gl_ClipDistance varying output enabled by glEnable(GL_CLIP_DISTANCE0). Once enabled, the primitive is clipped such that gl_ClipDistance[0] >= 0 after the vertex and geometry shaders.

The clip distance can be specified as just a dot product with a world-space plane equation:

http://github.prideout.net/clip-planes/

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文