OpenGL ES 2.0中的剪切平面

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了OpenGL ES 2.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中进行平面剪裁?

答案

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

  1. 使用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. 使用Alessandro Boccalatte的四倍深度缓冲: 禁用彩色书写(即设置glColorMask(false, false, false, false);) 渲染一个与标记形状匹配的四边形(即只是具有相同尺寸和标记位置/方向的四边形);这只会被渲染到深度缓冲区中(因为我们在上一步中禁用了颜色缓冲区写入) 启用颜色掩码(glColorMask(true, true, true, true);) 渲染您的3D模型
另一答案

由于扩展EXT_clip_cull_distance在OpenGL ES 2.0中不可用(因为此扩展需要OpenGL ES 3.0),因此必须模拟剪切。它可以通过丢弃片段在片段着色器中进行模拟。见Fragment Shader - Special operations

另见OpenGL ES Shading Language 1.00 Specification; 6.4 Jumps; page 58

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

if (intensity < 0.0)
    discard;

模拟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

以上是关于OpenGL ES 2.0中的剪切平面的主要内容,如果未能解决你的问题,请参考以下文章

iOS 上的 OpenGL ES 1.1:没有剪切平面?

用于模糊的 OpenGL ES 2.0 片段着色器速度慢且质量低

iOS上的OpenGL ES 1.1:没有剪裁平面?

如何使用 opengl 和 pyglet 设置剪切平面

OpenGL ES 2.0 (iOS) 中的 2D 绘图

OpenGL ES 2.0:似乎无法渲染第二个 VBO?