关于opengl:监听相机事件

今天更美妙onAfterCheckInputsObservableonDisposeObservableonProjectionMatrixChangedObservable投影矩阵用于投影变换,投影变换是三维场景中的物体正确渲染到二维屏幕的重要过程之一。在透视矩阵中,有几个重要元素:视场角、成像设施的宽高比、场景中能看到的最近间隔以及最远距离,通过这几个参数能够定义一个视锥体对象,从而模仿人眼或者相机的在三维空间中的成像原理,通常有这个几个值就能够结构一个4x4的矩阵,通过OpenGL提供的接口设置即可。应用形式:在理论开发场景中,咱们能够利用现有的库生成矩阵,也能够利用透视投影的根底原理计算出矩阵。 这里列出后面提到的几个重要参数:视场角:fov;成像设施的宽高比:aspect;近裁剪面间隔:znear;远裁剪面间隔:zfar。 onRestoreStateObservableonViewMatrixChangedObservable相机静止触发的事件只有相机产生了静止,就会推送告诉到办法回调里。与相机有间接关系

October 17, 2022 · 1 min · jiezi

关于opengl:OpenglEs之着色器

前言在后面咱们介绍了 [OpenglEs之EGL环境搭建]() ,在前面的例子中,咱们将无可避免地须要应用到着色器。而着色器才是Opengl的灵魂所在,有了着色器才有了Opengl天马行空的世界。 图形渲染管线要想了解什么是着色器以及着色器的作用就必须先理解下图形渲染管线。 在OpenGL中,任何事物都在3D空间中,而屏幕和窗口却是2D像素数组,这导致OpenGL的大部分工作都是对于把3D坐标转变为适应你屏幕的2D像素。将OpenGL中的3D世界转换到屏幕窗口中的2D世界中展现进去的过程称为图形渲染管线。也能够说将一堆原始图形数据途经一个输送管道,期间通过各种变动解决最终呈现在屏幕的过程就是图形渲染管线。图形渲染管线能够被划分为两个次要局部:第一局部把你的3D坐标转换为2D坐标,第二局部是把2D坐标转变为理论的有色彩的像素。 图形渲染管线能够被划分为几个阶段,每个阶段将会把前一个阶段的输入作为输出。所有这些阶段都是高度专门化的(它们都有一个特定的函数),并且很容易并行执行。正是因为它们具备并行执行的个性,当今大多数显卡都有成千上万的小解决外围,它们在GPU上为每一个(渲染管线)阶段运行各自的小程序,从而在图形渲染管线中疾速解决你的数据。这些小程序叫做着色器(Shader)。 着色器着色器是进行三维图形学编程的先进办法,从某种意义上来说 Shader 的呈现是图形学中的一种”退化”,因为在这之前所有的性能都间接由固定管线提供,而开发人员只须要为其指定参数(如光照属性、旋转角度等),然而因为 Shader 的呈现这些性能当初都须要开发者本人通过 Shader 实现。尽管如此,这种可编程性可能提供给开发者更多的灵活性和创造性。 下图是Opengl图形渲染管线的形象过程: 蓝色局部代表的是咱们能够注入自定义的着色器的局部。也就说咱们通过自定义顶点着色器、几何着色器和片段着色器就能够实现咱们想要各种成果,同时几何着色器又是可选的,个别应用默认的即可,因而咱们只需将重点放在顶点着色器和片段着色器即可。 上面参照上图,简略介绍一下各个阶段的次要性能职责: 顶点着色器 图形渲染管线的第一个局部是顶点着色器(Vertex Shader),它把一个独自的顶点作为输出。顶点着色器次要的目标是把3D坐标转为另一种3D坐标,同时顶点着色器容许咱们对顶点属性进行一些根本解决,例如矩阵变换等。图元拆卸 图元拆卸阶段将顶点着色器输入的所有顶点作为输出,并所有的点装配成指定图元的形态。例如绘制正方形时,就是通过两个三角形组装成正方形,绘制多面体时,就是将n多个三角形组装成多面体的过程。几何着色器 图元拆卸阶段的输入会传递给几何着色器。几何着色器把图元模式的一系列顶点的汇合作为输出,它能够通过产生新顶点结构出新的(或是其它的)图元来生成其余形态。光栅化 几何着色器的输入会被传入光栅化阶段,这里它会把图元映射为最终屏幕上相应的像素,生成供片段着色器(Fragment Shader)应用的片段(Fragment)。在片段着色器运行之前会执行裁切(Clipping)。裁切会抛弃超出你的视图以外的所有像素,用来晋升执行效率。片段着色器 片段着色器的次要目标是计算一个像素的最终色彩,这也是所有OpenGL高级成果产生的中央。通常,片段着色器蕴含3D场景的数据(比方光照、暗影、光的色彩等等),这些数据能够被用来计算最终像素的色彩。测试与混合 这个阶段检测片段的对应的深度(和模板(Stencil))值,用它们来判断这个像素是其它物体的后面还是前面,决定是否应该抛弃。这个阶段也会查看alpha值(alpha值定义了一个物体的透明度)并对物体进行混合(Blend)。所以,即便在片段着色器中计算出来了一个像素输入的色彩,在渲染多个三角形的时候最初的像素色彩也可能齐全不同。GLSLOpenGL着色器是用OpenGL着色器语言(OpenGL Shading Language, GLSL)写成的。 在OpenGL中,咱们必须定义至多一个顶点着色器和一个片段着色器,因为GPU中没有默认的顶点/片段着色器。 咱们来看一个最简略的顶点着色器的例子: #version 300 esin vec4 aPos;void main(){ gl_Position = aPos;}GLSL的语法与C语言很相似,置信有过编程教训的搭档们大略都能看懂以上这个着色器。 顶点着色器的第一行#version 300 es首先申明了着色器版本号,300的意思是代表着色器语言须要是3.0之后的版本。 第二行in vec4 aPos;申明了一个名为aPos的4个重量的输出变量。其中int代表的是定义输出变量,也就是说能够利用这个变量通过CPU向GPU传递数据,与之对应的是应用out关键字申明的输入变量。留神,int和out是Opengl 3.0之后所做的批改,在Opengl 2.0中与之对应的关键字是attribute和varying,也就是说在Opengl 3中attribute改成了in而varying改成了out。 前面定义的就是一个main函数,这个没什么好解析的,就像所有语言程序一样,作为程序的一个入口,而gl_Position是Opengl中顶点着色器的一个内建输入变量,代表的是最终通过解决后的顶点坐标。 上面咱们再来看一个简略的片段着色器的例子: #version 300 esprecision mediaump float;out vec4 FragColor;void main(){ FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);} 正如顶点着色器那样,片段着色器的第一行也是申明着色器的版本。第二行precision mediaump float;则是申明着色器中浮点变量的默认精度。 在Opengl ES 3的版本中咱们须要手动地应用out关键字申明输入着色器的色彩变量,例如上述例子中的第三行,而在Opengl ES2中这不是必须的,因为在Opengl ES2中间接应用内置的变量gl_FragColor即可。至于为什么这样做,笔者也不得而知... ...

October 11, 2022 · 1 min · jiezi

关于opengl:OpenGL-shader-程序基础

什么是 shader ?Shader 是一品种C语法的程序,用于隔离硬件相干局部的的代码。不同的shader程序在应用时编译老本机硬件反对的程序。 shader 分两种: 顶点渲染程序 vertex shader片断渲染程序 fragment shadervertex shader 用于批改(平移,旋转,扭曲)顶点的地位: potision 和确定顶点的色彩fragment shader 用于确定每个像素的色彩,应用照明,材质等等 编写 shader 程序编译 vertex shader,到ID编译 fragment shader,失去ID查看是否有编译谬误链接以上两个 shader 程序, 失去ID 放弃这个ID渲染三角形时,应用这个ID不同的模型能够有不同的shader程序举个例子顶点渲染器 vertex shader: in vec4 s_vPosition;void main () { // 看,我不须要任何矩阵操作 // s_vPosition 的值须要在 -1.0 和 1.0 之间 gl_Position = s_vPosition;}out vec4 s_vColor;void main() { //不管三七二十一,像素都搞成红的! fColor = vec4 (1.0, 0.0, 0.0, 1.0);}与C语言一样,main函数是必须的入口函数。双斜杆为正文。 编译渲染器能够通过调用一些OpenGL函数来编译渲染器程序。 编译渲染器分三步: 创立id,绑定代码, 编译。 <GLuint> glCreateShader (<type>) 创立一个渲染器ID, 具备 GLuint 类型如 GLuint ID = glCreateShader(GL_VERTEX_SHADER);glShaderSource (<id>, <count>, <src code>, <lengths>) ...

February 10, 2022 · 1 min · jiezi

关于opengl:拍乐云发布互动白板20首创超高清文档演示和滚动浏览

在线合作白板平台「Miro」近日发表取得新一轮4亿美元融资,估值达到175亿美元,这则音讯让更多人关注互动白板这个赛道以及背地的核心技术。 拍乐云互动白板作为实时音视频的能力补充,能够疾速实现利用内的多人实时轨迹同步、画笔图形等多样化工具、音视频文件播放、动效PPT出现等能力。 本次降级公布的互动白板2.0,又推出了业内首个文档超高清演示、自定义图章工具等能力,很好地解决了开发者在自研白板时遇到的一些痛点和难点,包含技术底层投入大、转码成功率低、互动体验差等等,帮忙企业、机构和SaaS服务商解决这些难题,把更多的精力专一在本身外围业务上,实现实时互动和信息交互的全面降级。 亮点性能 1本次互动白板2.0公布的外围亮点性能之一是超高清文档演示和滚动浏览,矢量缩放不失真,反对ppt、pdf、doc等文档格局。与之前将文档转为图片格式相比,转为PDF格局能大大晋升文档的压缩品质,实现更高清晰度的滚动展现成果,加强教学、培训、会议等场景的用户体验。白板文档有两种类型,别离是动态文档和动静文档。常见的动态内容是通过背景图实现的,但图片受限于其固定分辨率,缩放后的体验并不现实,PDF格局则能较好地保留内容信息,缩放后仍能放弃较高清晰度。另外,常见的翻页浏览形式并不适宜Word等长文档内容,滚动浏览的交互体验更佳。滚动浏览的成果在地位同步、渲染性能等方面的要求更高,对此,互动白板2.0也做了更加精密的调校。 亮点性能 2互动白板2.0的第二个亮点性能是个性化图章互动工具。老师在教学过程中心愿可能及时激励学生,或者标注出讲课的难点,利用点赞、爱心、火箭等图章就能够疾速地互动,加强课堂场景的趣味性。而在企业培训和视频会议场景中,利用五角星等图章疾速标注重点也会十分便捷,不同的行业客户还能够设计自定义图章通过接口传入,在行业场景中发明更具个性化的互动表白工具。针对自定义内容如何实现云端录制推流和多端同步也是在开发中必须要思考的难点,互动白板2.0既放弃了SDK的易用性,又丰盛了互动白板的应用场景。 拍乐云创始人赵加雨补充道,“图章工具的这个灵感最早来自于一个钢琴陪练的客户场景,客户冀望可能定制一个“Good”的图章,为了满足更个性化、体验更好的性能实现,咱们研发了自定义图章工具。” 亮点性能 3除了这两个亮点外,互动白板2.0在文档转码速度和转码成功率上有了十分多的优化,其中动态文档的转码成功率高达99.9%、动静文档的转码成功率高达98.5%。文档转码在互动白板中是一个绝对简单的性能,波及字体、媒体格式、多种动效、场景格局等,须要一直适配,解决泛滥问题,至多包含: 解决转码速度慢,体验不佳的问题禁用主动翻页性能,解决不同步和重影问题解决字体偏移问题,应用常见零碎字体,少用偏门罕见字体解决文本手动旋转横竖颠倒问题解决转码字体含糊问题解决翻页动画组错乱导致转码失败问题互动白板2.0在文档转码速度和转码成功率上一直优化技术,基于客户场景打磨出更好的产品体验。 亮点性能 4第四个亮点是互动白板2.0已反对内部H5文档。教育场景的课件多种多样,不同厂商的课件有不同的交互协定,有些还带有自定义动画成果。为兼容各种H5文档,PanoExternalHtml SDK封装了白板内文档同步的罕用指令,并提供自定义音讯通道供开发者应用,十分地灵便易用。利用开发者只须要将本人的文档外包一层PanoExternalHtml SDK的壳,即可导入拍乐云互动白板,由拍乐云实现各端的文档同步。对于曾经有文档同步机制或某些非凡需要的客户,拍乐云也反对通过通明白板,对用户的自定义内容实现实时标注和同步。 H5课件 互动白板逐步成为了线上实时互动和合作沟通的刚需,其应用场景也越来越丰盛。互动白板2.0的公布,是拍乐云打磨产品竞争力、赋能行业客户的重要一步。咱们心愿通过提供更优质的产品计划,深刻更丰盛的业务场景,实现从“信息传递”到“信息交互”的降级,让企业开好会、让老师上好课、让合作更高效、让社交更乏味。 如果您想要进一步体验产品,能够下载PVC Demo(Demo下载_SDK下载-拍乐云 Pano),举荐应用macOS & Windows端。

January 27, 2022 · 1 min · jiezi

关于opengl:使用3D-API进行2D描线的一个抗锯齿方法

背景这篇文章针对的是须要从3D硬件加速API自撸2D绘制引擎的状况。 在3D引擎中绘制比较复杂的2D线条,常见的办法是在CPU中先算出线条的具体轮廓(在此过程中思考简单的线条款式,如线条拐角与端点的形态等等)并三角化,而后将三角形图元发送到显卡管线进行渲染。显卡起的作用其实次要是光栅化器和执行着色逻辑。 线条地位,线条轮廓,三角化图元,光栅化。那么咱们立刻就会遇到抗锯齿(antialias,以下简称AA)的问题。3D管线内置的MSAA成果比较稳定,然而计算代价较大,而更先进的FXAA、深度学习抗锯齿更多地是为了3D场景渲染设计,如果利用到2D绘制,一是未必有它们须要的信息(比方深度、光照),二是这些近似算法可能不太适宜用在要求准确的2D绘制上。而且不论如何,咱们常常会遇到不能关上硬件内置AA的状况(硬件不反对/须要重开窗口而利用场景无奈重开窗口)。于是一个自制的AA总是有必要的。 此处介绍一种比拟简便的抗锯齿描线办法,它大体上是对一个传统的三次draw call描线办法(别离绘制线条核心和半透明的两个边际)的一个改良,次要有这些益处: 三角形数量大抵上只有原始形态的两倍,而不是三倍。只须要一次draw call。思路比较简单。计算代价绝对较低。不依赖任何独有的硬件个性。成果能够承受。办法在栅格化的时候,AA操作的本质,是计算被矢量图形局部笼罩的像素该当是什么“强度”,比方:如果像素有一半的面积被笼罩,那它的“强度”该当是50%;有三分之一被笼罩,它的“强度”就该当是33%。那么如何搞出一个比拟适合的“强度”呢?这个办法的思路是:让像素依照本人核心间隔线条边界的间隔逐步淡出:处于边界外部0.5像素尺寸或更靠内的像素有齐全的“强度”,处于边界内部0.5像素尺寸的像素是齐全淡出的。这是对像素覆盖率的一个近似。 为了达到这个目标,首先咱们在生成线条的几何轮廓的时候,进行下列两点修改: 光栅化的时候,不思考任何硬件AA,那么只有核心在几何体之内的像素才会参加渲染。为了保障仅局部笼罩的像素也参加渲染,线条的几何轮廓须要略微搞大一点。如果线条宽度为w,半径r = w/2,那么改为生成r+1半径的轮廓(实际上该当+0.707像素尺寸就够了)。这是为了保障淡出范畴的像素被笼罩。生成轮廓的时候,劈成左右两半边,并且赋予一个顶点属性记录本人离线条核心有多远:在线条核心的顶点设置为0,在边缘的顶点设置为r+1,让它插值到fragment shader。这样天然就晓得每个像素离核心多远了。这个过程相当于构建了一个矢量的有向间隔场。 粗实线为线条核心,深灰色标识原始线条宽度,浅灰色标识“扩大”的线条宽度。最终在fragment shader中,根据这个属性计算出片元离边界的间隔,归一化之后间接给片元色彩额定的alpha系数,两者关系如下图所示: 下图为应用了这个办法进行AA的理论渲染成果。其中绿色折线宽度为2像素,蓝色曲线宽度为1像素。 一些显然的改良DPI-aware其实非常简单,算alpha的时候假如像素尺寸不是1就好了:如果DPI缩放是2,那么像素尺寸就是0.5,并依此计算像素“强度”。于是alpha值和核心间隔的关系稍作批改,如下图所示: DPI缩放值没法间接从shader中取得,须要从另外的中央拿到(比方窗口零碎),并在调用时塞到uniform里。 更正确的像素“强度”上述简化的AA算法的像素“强度”估算,其实只在线条方向平行于像素边际时才是正确的,其它方向都会有一些偏差,会导致细曲线看上去有些奇怪。 齐全准确的像素“强度”其实也并不那么艰难,但须要额定的信息:在生成几何体的时候,把线条的切线方向传进顶点,并插值到片元。这样一来,片元同时晓得本人离边缘的间隔和切线的方向,是能正确计算出本人的覆盖率的,依此算出的像素“强度”基本上是准确的。

January 5, 2022 · 1 min · jiezi

关于opengl:Opengl混合算法探究

在咱们的理论利用中应用OpenGL进行混合常见的问题有以下三种: 应用Opengl自带的混合算法自定义混合算法半透明混合针对以上三种状况咱们具体分析有何不同及如何解决问题。一、Opengl自带混合算法OpenGL渲染管线的最初阶段会将源色和底色进行混合,咱们大部分状况下只需思考实现此次drawcall的渲染实现即可,无心过分操心如何与底色进行混合。那么如何应用自带的混合呢? 首先咱们须要开启混合模式,开启混合模式之后即可应用OpenGL自带的混合算法。glEnable(GL_BLEND); 开启混合之后如何混合源色彩与底色呢?混合须要设置两个函数,一是设置混合因子即源色彩与底色各按何种比例进行混合,另一个就是混合函数,决定源色彩与底色应用哪种运算符失去后果。同时RGB色彩也能与Alpha值别离设置不同得混合因子与混合函数。 //设置源颜色混合因子与底色混合因子glBlendFunc(GLenum sfactor,GLenum defector);//示例glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);//别离设置RGB色彩与Alpha值得混合因子glBlendFuncSeparate(GLenum srcRGB,GLenum dstRGB,GLenum srcAlpha,GLdstAlpha)//示例glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ZERO);//设置混合函数glBlendEquation(GLenum mode);//示例glBlendEquation(GL_FUNC_ADD);//别离设置RGB色彩与Alpha值得混合函数glBlendEquationSeparate(GLenum modeRGB, GLenum modeAlpha);//示例glBlendEquationSeparate(GL_FUNC_ADD, GL_FUNC_ADD);下面得代码示例示意:源色彩(RGB或RGBA)须要乘源色彩得alpha,底色(RGB或RGBA)须要乘以一减源色彩得alpha,而混合函数为GL_FUNC_ADD,所以最终混合算法为: //未别离设置Color = vec4(Srgb * Sa + Drgb * (1 - Sa), Sa * Sa + Da * (1 - Sa));//别离设置RGB与Alpha得混合Color = vec4(Srgb * Sa + Drgb * (1 - Sa), Sa + Da * (1 - Sa));具体得混合因子与混合函数参考OpenGL混合示例,不再赘述。惟一须要留神得色彩是否预乘alpha得问题,因为默认也是最罕用得混合模式后果是预乘的,因而如果想要失去未预乘的后果,自带的混合算法并不实用。总的来说,自带混合算法比拟不便速度也比拟快能够满足大部分的应用场景,但性能反对无限有些需要无奈满足。 二、自定义混合算法下面讲到如果要失去未预乘的后果Opengl自带混合算法无奈为咱们失去正确的后果,此时咱们就须要敞开OpenGL自带的混合模式在shader中本人实现混合算法。PhotoShop与AE中都有混合模式这个性能,有很多的混合模式可选,但因为OpenGL自带的混合算法反对的性能无限,这些混合算法都无奈间接通过OpenGL自带的混合算法实现。手动实现混合算法比拟自在,咱们能够自定义一些混合办法,实现一些OpenGL自带混合模式无奈实现的简单混合算法,毛病是在大部分GPU上同一个texture无奈既作FBO输入,又作纹理采样输出,如果底图作为输出传入Fragment Shader,则以后FBO须要绑定另一个texture作为输入。如果混合区域笼罩全图,能够用FBO绑定一个空的texture作为输入,同时原始底图传入Fragment Shader作为输出;如果混合区域只占全图的一部分,那么就须要首先复制一份底图纹理并绑定到FBO作为输入,同时原始底图纹理传入Fragment Shader做混合,这两种不同的混合场景下,不论混合区域是全图还是局部区域,都须要申请一块额定的底图大小的纹理存储(空白或复制底图),另外局部区域混合时还须要一次额定的渲染(复制底图),混合所须要的空间和工夫都有额定开销。尽管实现了咱们的需要,但毛病也是不言而喻的,工夫与空间的开销都比拟大。 一些罕用的自定义混合算法能够从这里找到 三、半透明混合在3D渲染中,深度缓冲去十分重要,深度值决定了以后片元是否须要抛弃,如果通明物体与半透明物体放在一起渲染就可能会呈现半透明物体没有与背地的物体进行混合。例如 产生这一景象的起因是,深度测试和混合一起应用的话会产生一些麻烦。当写入深度缓冲时,深度缓冲不会查看片段是否是通明的,所以通明的局部会和其它值一样写入到深度缓冲中。后果就是不思考透明度都会进行深度测试。即便通明的局部应该显示背地的物体,但深度测试依然抛弃了它们。这也是混合变得有些麻烦的局部。要想保障窗户中可能显示它们背地的窗户,咱们须要首先绘制背地的这部分窗户。这也就是说在绘制的时候,咱们必须先手动将窗户依照最远到最近来排序,再依照程序渲染。 但这依然不能解决所有问题,因为物体在3d空间中可能会穿插导出咱们无奈确定程序,甚至咱们都无奈确定物体是否为半透明的。这样看来这也不是一个好的解决办法。那么如果咱们能逐像素对所有的渲染指标进行排序,这样咱们就能分清远近关系。这就须要我之前一篇文章提到的OIT渲染。 半透明混合是十分辣手的,如果想要失去好的成果必然带来性能或空间的巨大损失,OIT渲染的将来仍须要提出更好的计划及硬件反对。

December 7, 2021 · 1 min · jiezi

关于opengl:图片处理技巧-image-magick

应用 image magick, 批改图片背景色 ➜ src git:(main) ✗ convert /Users/jzd/Movies/A_a/Clang/src/Cache_t.png \ -fill white -fuzz 20% \ -draw 'color 0,0 floodfill' \ output_color.png

June 4, 2021 · 1 min · jiezi

关于opengl:Single-Depth-peeling-顺序无关渲染OIT

什么是程序无关渲染 在3D渲染中,物体的渲染是按肯定的程序渲染的,这也就可能导致半透明的物体先于不通明的物体渲染,后果就是可能呈现半透明物体后的物体因为深度遮挡而没有渲染进去。对于这种状况通常会先渲染所有的不通明物体再渲染半透明物体或者按深度进行排序来解决。但这样依然无奈解决半透明物体之间的通明成果渲染谬误问题,特地是物体之间存在穿插无奈通过简略的排序来解决。于是就有一些用专门来解决半透明物体渲染算法,OIT算法即Order Independent Transparency(程序无关的半透明渲染)。Depth Peeling是泛滥OIT算法里能够失去准确blending后果的一个,在非游戏的3d利用场景中应该还是很有价值的。 两个穿插半透明四边形(未应用OIT渲染) 两个穿插半透明四边形(应用OIT渲染) Single Depth Peeling原理 Single Depth Peeling原文 Single Depth Peeling 顾名思义,就是通过屡次绘制,每次绘制剥离离相机最靠近的一层,像剥洋葱一样层层剥开,按程序混合就失去了准确的混合后果。既然有Single Depth Peeling,还有一种优化版本就是Dual Depth Peeling,从前后两个方向剥离,不在本次探讨的范畴,有趣味能够参考链接论文。 深度剥离是一种对深度值进行排序的技术。它的原理比拟直观,通常的深度检测是将场景中Z值最小的像素输入到屏幕上,就是里相机最近的像素。如此一来就肯定有离相机第二近的点,第三近的点·····。通过屡次渲染的办法,第一次失常渲染,将深度值存入纹理就失去来离相机最近像素的深度和色彩。第二遍渲染时,把每个像素的深度与上次的深度值做比拟,但凡小于上次深度值的都通过测试,在加上FBO深度测试的最小值性能就能失去下一个最小的深度值与色彩值,以此类推即可。 毛病 须要剥离N次能力实现,就须要N个Pass,N是深度复杂度。因而性能是重大的瓶颈,另外如何确定N也是个问题。 具体流程 1、创立两对色彩纹理和两对GL_FLOAT类型的深度纹理用来pingpong。 2、clear深度纹理为0,敞开OpenGL混合 2、失常渲染,大于深度纹理上的值都能够通过测试,加上深度缓冲测试的最小深度值就能够失去离相机最近的深度与色彩值。将色彩后果与色彩纹理中的色彩做混合,深度写入深度纹理。 3、应用上次失去的色彩与深度作为输出纹理反复2的操作,直到剥离实现。 如何从前向后混合色彩 从前向后间接混合显著是谬误的,然而咱们能够依据混合算法推导出反向混合的算法,具体推导能够参考Dual Peth Peeling的paper。具体混合算法为: glBlendEquation(GL_FUNC_ADD); glBlendFuncSeparate(GL_DST_ALPHA, GL_ONE, GL_ZERO, GL_ONE_MINUS_SRC_ALPHA);如何确定N 咱们无奈确定须要剥离多少次,因为不同的渲染指标的深度复杂度是不同的。目前来说最好的办法是采纳遮挡查问的形式来检测是否剥离实现。但这种形式须要GPU同步,也会带来重大的性能问题。形式如下 GLuint queryId;glBeginQuery(GL_SAMPLES_PASSED, queryId);//depth peelingglEndQuery(GL_SAMPLES_PASSED);GLuint queryReady = GL_FALSE;glGetQueryObjectuiv(queryId, GL_QUERY_RESULT_AVAILABLE, &queryReady);GLuint samples = 0;glGetQueryObjectuiv(mOITQueryId, GL_QUERY_RESULT, &samples);samples为0时就剥离实现了,不能0则持续剥离。 理论利用中值得注意的中央 因为深度精度问题可能会造成穿插的中央有接缝,具体做法如下: 1、深度缓冲及纹理应用GL_FLOAT类型减少精度。 2、纹理须要应用高精度的纹理 precision highp sampler2D; 3、离摄像机过于依然会因为精度有余而呈现接缝,这时就须要动静调整摄像机远近立体来晋升精度 4、优化遮挡查问中的同步操作 5、防止遮挡查问呈现死循环 ...

November 23, 2020 · 1 min · jiezi

OpenGL初探Demo绘制正方形

1. 引入所须要的库#include<GLShaderManager.h> 移入了GLTool 着色器管理器(shader Mananger)类。没有着色器,咱们就不能在OpenGL(外围框架)进行着色。着色器管理器不仅容许咱们创立并治理着色器,还提供一组“存储着色器”,他们可能进行一些初步和根本的渲染操作。 #include "GLShaderManager.h"#include<GLTools.h> GLTool.h头文件蕴含了大部分GLTool中相似C语言的独立函数。 #include "GLTools.h"在Mac 零碎下,间接#include<glut/glut.h> #include <GLUT/GLUT.h>2. 设置全局变量定义一个着色管理器,如下: GLShaderManager shaderManager;定义一个简略的批次容器,是GLTools的一个简略的容器类。 GLBatch triangleBatch;设置边长: GLfloat blockSize = 0.1f;初始化正方形的4个点坐标,别离x,y,z轴: GLfloat vVerts[] = { -blockSize,-blockSize,0.0f, blockSize,-blockSize,0.0f, blockSize,blockSize,0.0f, -blockSize,blockSize,0.0f};定义正方形偏移量: GLfloat xPos = 0.0f;GLfloat yPos = 0.0f;3. 程序入口函数main()int main(int argc,char *argv[]){ //设置当前工作目录,针对MAC OS X /* `GLTools`函数`glSetWorkingDrectory`用来设置当前工作目录。实际上在Windows中是不必要的,因为工作目录默认就是与程序可执行执行程序雷同的目录。然而在Mac OS X中,这个程序将当前工作文件夹改为应用程序捆绑包中的`/Resource`文件夹。`GLUT`的优先设定主动进行了这个中设置,然而这样中办法更加平安。 */ gltSetWorkingDirectory(argv[0]); //初始化GLUT库,这个函数只是传说命令参数并且初始化glut库 glutInit(&argc, argv); /* 初始化双缓冲窗口,其中标记GLUT_DOUBLE、GLUT_RGBA、GLUT_DEPTH、GLUT_STENCIL别离指 双缓冲窗口、RGBA色彩模式、深度测试、模板缓冲区 --GLUT_DOUBLE`:双缓存窗口,是指绘图命令实际上是离屏缓存区执行的,而后迅速转换成窗口视图,这种形式,常常用来生成动画成果; --GLUT_DEPTH`:标记将一个深度缓存区调配为显示的一部分,因而咱们可能执行深度测试; --GLUT_STENCIL`:确保咱们也会有一个可用的模板缓存区。 */ glutInitDisplayMode(GLUT_DOUBLE|GLUT_RGBA|GLUT_DEPTH|GLUT_STENCIL); //GLUT窗口大小、窗口题目 glutInitWindowSize(400, 300); glutCreateWindow("Square"); /* GLUT 外部运行一个本地音讯循环,拦挡适当的音讯。而后调用咱们不同工夫注册的回调函数。咱们一共注册2个回调函数: 1)为窗口扭转大小而设置的一个回调函数 2)蕴含OpenGL 渲染的回调函数 */ //注册重塑函数 glutReshapeFunc(changeSize); //注册显示函数 glutDisplayFunc(RenderScene); //注册非凡函数 glutSpecialFunc(SpecialKeys); /* 初始化一个GLEW库,确保OpenGL API对程序齐全可用。 在试图做任何渲染之前,要查看确定驱动程序的初始化过程中没有任何问题 */ GLenum status = glewInit(); if (GLEW_OK != status) { printf("GLEW Error:%s\n",glewGetErrorString(status)); return 1; } //设置咱们的渲染环境 setupRC(); glutMainLoop(); return 0;}4. 重要函数在main函数中,咱们除了零碎的函数,咱们还定义了4个十分重要的函数,例如: ...

July 15, 2020 · 2 min · jiezi

OpenGL常用术语知多少

1. 什么是OpenGLOpenGL是一个跨语言、跨平台的编程图形程序接口,它将计算机的资源形象为一个个的OpenGL的对象,对这些资源的操作形象为一个个OpenGL的指令。它的移植性高,并且速度十分快。 2. OpenGL状态机状态机是一种形象的模型,示意一组状态变量的汇合。它形容了一个对象在其生命周期内所经验的各种状态,状态之间的转变,产生转变的动因、条件以及转变中所执行的流动。比方:色彩、纹理坐标、源因子和指标因子、光源的各种参数,这些都是状态,另外还有:是否开启了光照,是否开启了纹理,是否开启了混合,是否开启了深度测试等等。OpenGL会放弃这些状态,除非咱们调用函数来扭转这些状态。 3. OpenGL上下文OpenGL上下文是一个十分形象的概念,能够了解为一个蕴含了所有OpenGL状态的对象。在咱们调用任何OpenGL指令前,都须要先创立一个上下文,这个上下文记录了OpenGL渲染所须要的所有信息以及状态,所以上下文也是一个宏大的状态机。 4. 渲染所谓的渲染,就是将数学和图形数据转换成3D空间图像的操作叫做渲染(Rendering)。 5. 管线在OpenGL中,3D坐标转为2D坐标的处理过程是由OpenGL的图形渲染管线(Graphics Pipeline,大多译为管线,实际上指的是一堆原始图形数据途经一个输送管道,期间通过各种变动解决最终呈现在屏幕的过程)治理的。图形渲染管线能够被划分为两个次要局部:**(1)第一局部把你的3D坐标转换为2D坐标。(2)第二局部是把2D坐标转变为理论的有色彩的像素。** 在OpenGL 下渲染图形,就会经验一个一个节点,而这样的操作能够了解为管线。大家能够设想成流水线,每个工作相似流水线般执行。工作之间有先后顺序。 管线是一个形象的概念,之所以称之为管线是因为显卡在解决数据的时候是依照一个固定的程序来的,而且严格依照这个程序,这个程序就是渲染流程,而管线指的是这个过程。6. 固定管线/存储着色器OpenGL本身为开发者封装了很多着色器(shader)程序,开发者只须要调用API即可,不须要关怀外部的实现过程,而这些封装好的程序(函数或办法)即为固定管线。在固定管线下,应用固定存储着色器,固定存储着⾊器由GLTools的C++类GLShaderManager治理,它们可能满⾜进⾏根本渲染的根本要求。然而因为OpenGL 的应用场景十分丰盛,固定管线或存储着色器无奈实现每一个业务,这时将相干局部凋谢成可编程。 7. 着色器程序(Shader)顾名思义,着色器程序就是一个程序代码,在OpenGL调用绘制函数之前,须要制订一个着色器,在固定管线下,这个着色器曾经是封装好的了,在可编程的状况下,则须要程序员本人编写着色器程序。常见的着色器有: 顶点着色器 (VertexShader)片元着色器(FragmentShader)/像素着色器 (PixelShader)几何着色器曲面细分着色器在 OpenGL ES 3.0后,可编程仍然只反对了顶点着色器和片段着色器这两个最根底的着色器。 7.1 顶点着色器(VertexShader)顶点着色器是一组指令代码,这组指令代码在顶点被渲染时执行,个别用来解决图形每个顶点变换(旋转/平移/投影等)。顶点着色器是OpenGL中用于计算顶点属性的程序。顶点着色器是逐顶点运算的程序,也就是说每个顶点数据都会执行一次顶点着色器,当然这是并行的,并且顶点着色器运算过程中无法访问其余顶点的数据。一般来说典型的须要计算的顶点属性次要包含顶点坐标变换、逐顶点光照运算等等。顶点坐标由本身坐标系转换到归一化坐标系的运算,就是在这里产生的。 7.2 片元着色器(FragmentShader)个别用来解决图形中每个像素点色彩的计算和填充。片段着色器是OpenGL中用于计算片段(像素)色彩的程序。片段着色器是逐像素运算的程序,也就是说每个像素都会执行一次片段着色器,这个过程也是并行的。 7.3 着色器渲染过程 8. 光栅化(Rasterrization)又名栅格化或者像素化,其实就是把矢量图转化成像素点的过程。咱们都晓得三维物体是由点线面形成的,将这些点线面转化成屏幕上的像素点,这个过程就是光栅化。该过程包含了两局部工作: 决定窗口坐标中哪些整型栅格区域被根本图元占用;调配一个色彩值和一个深度值到各个区域。光栅化过程产生的是片元。 9. 纹理(texture)在OpenGL中,咱们所说的纹理能够了解成是一张图片,在渲染图形的时候,须要将图片贴在其外表,使其看起来更真切。 10. 混合(Blending)混合就是将源色和指标色彩通过某种形式混合生成特效的技术,艰深一点就是将两种色彩通过某种算法生成非凡的成果。混合通常用来绘制通明或者半透明的物体。 11. 变换矩阵(Transformation)如果图形想产生平移、缩放、旋转变换,就须要应用变换矩阵。 12. 投影矩阵(Projection)用于将3D坐标转换为二维屏幕坐标,理论线条也将在二维坐标下进行绘制。 13. 2D笛卡尔坐标系 14. 3D笛卡尔坐标系 15. 视口 16. 投影形式在OpenGL中,次要有两种投影形式,第一种是正投影或叫平行投影,第二种是透视投影。在应用正投影的时候,须要筹备一个正方形或者长方形的视景体(屏幕)。视景体之外的任何物体都不会被绘制,而是所有理论物体的大小和视景体内的大小都雷同,无论远近。在应用透视投影的时候,远处的物体看上去会比近处的物体小一些,合乎近大远小的原理。越凑近视景体,投影越靠近物体尺寸,反之,越远,则越小于物体自身的尺寸。

July 12, 2020 · 1 min · jiezi

图像库-libpng-编译与实践

在之前的文章中介绍了 stb_image 图像库,还顺带提到了 libpng 和 libjpeg ,这篇文章就是介绍如何在 Android 平台上用 CMake 编译 libpng 动态库以及 libpng 使用实践。 【简单易用的图像解码库介绍 —— stb_image】 https://glumes.com/post/android/stb-image-introduce/ <!--more--> libpng 介绍libpng 的官方介绍网站如下: http://www.libpng.org/pub/png/libpng.html下载地址网站如下: https://sourceforge.net/projects/libpng/files/博客中使用的版本是 1.6.37 ,也是目前最新的版本了。 关于 libpng 的编译网上已经有不少博客教程了,但有的是基于 Linux,有的是基于 Android.mk 的,本文会介绍如何在 Android Studio 上通过 CMake 来编译 Android 的动态库。 CMake 编译 libpng 动态库neon 相关编译在 libpng 的源代码中,就提供了 CMakeLists.txt 文件用以说明如何编译,但是却不能直接用在 Android 平台上,不过可以借鉴其源码作为参考。 由于 CMake 跨平台编译的特性,一般大型项目代码编译都会针对平台做适配,常见代码结构如下: if (CMAKE_SYSTEM_PROCESSOR MATCHES "^arm" OR CMAKE_SYSTEM_PROCESSOR MATCHES "^aarch64") set(libpng_arm_sources arm/arm_init.c arm/filter_neon.S arm/filter_neon_intrinsics.c arm/palette_neon_intrinsics.c) // 定义宏 add_definitions(-DPNG_ARM_NEON_OPT=2) endif ()这段代码就是判断系统处理器平台,不同平台所需要编译的代码不一样。而 libpng 会有这样的适配,主要是因为它用到了 neon 相关优化,该优化主要是用在 filter 操作方面。 ...

July 15, 2019 · 3 min · jiezi

Android-NDK-开发-从-Assets-文件夹加载图片并上传纹理

原文链接:Android NDK 开发 —— 从 Assets 文件夹加载图片并上传纹理在 OpenGL 开发中,我们要渲染一张图片,通常先是得到一张图片对应的 Bitmap ,然后将该 Bitmap 作为纹理上传到 OpenGL 中。在 Android 中有封装好的 GLUtils 类的 texImage2D 方法供我们调用。 public static void texImage2D(int target, int level, int internalformat, Bitmap bitmap, int type, int border)该方法的底层原理实际上也是解析了该 Bitmap ,得到了 Bitmap 所有的像素数据,类似于 Android NDK 关于 Bitmap 操作的 AndroidBitmap_lockPixels 方法,如果你不太了解该方法,可以参考这篇文章:Android JNI 之 Bitmap 操作。 得到了所有像素数据之后,实际最终还是调用了 OpenGL 的 glTexImage2D 来实现纹理上传。当然,如果可以直接得到所有数据,也不需要走解析 Bitmap 这一步了,这种场景最常见的就是把相机作为输入了。 <!--more--> 接下来我们会通过 Android NDK 开发中去渲染一张图片,步骤还是如上,从图像解析到纹理上传,不同的是我们将会解析 Assets 文件夹中的图片,而不是一张已经保存在手机 SDCard 上的图片。 ...

May 15, 2019 · 1 min · jiezi

Stencil Test的应用总结

前言一直以来,对Stencil的Operation知其然而不知其所以然,不太明白提供这些Operation更新Stencil有什么用。而GPU的Stencil更新机制其实是根据应用的需求才这么设计的,理解好Stencil的应用情况,才能理解好Stencil Test的更新机制。因此,本文将对其主要的应用做下梳理,增强对Stencil Test的认知。1. Stencil Test简介在OpenGL/Direct3D的流水线中,Stencil Test被归入Pixel Shader之后的Output Merger Stage,其处理单位是像素(如果MSAA打开,则是Sample)。Stencil Test的有两个要点:Stencil值的测试,用于剔除像素Stencil值的更新,用于产生实现特定效果的Stencil值Stencil值的测试很简单——从Stencil Buffer 里读出该像素的Stencil值(8bit的UINT)与参考值比较,满足比较条件则pass最终画出(假设能通过Depth Test或其他剔除),否则fail直接剔除。比较函数以及参考值都是通过API设定,例如OpenGL的glStencilFunc(GLenum func, GLint ref, GLuint mask)函数。与Depth Test的比较函数类似,Stencil Test的比较函数包括NEVER, LESS, LEQUAL, GREATER, GEQUAL, EQUAL, NOTEQUAL和ALWAYS。 通过Stencil值的测试我们可以限制渲染的区域,比如下面的例子把渲染区域限制为Stencil值等于1的区域。 图1 给定中间图片中的Stencil值,将比较条件设为EQUAL,参考值设为1时,左侧图片的color通过Stencil Test后。 我们看到,只要Stencil Buffer里存储了期望的Stencil值,我们就可以通过Stencil Test剔除像素来画出期望的区域,正如Stencil本身的含义(模板)。而事实上问题重点常在于如何构造出期望的Stencil值,除了少数应用使用特定已知的模板外,大部分是在渲染过程中产生需要的模板,这就是要讲的第二个要点——Stencil值的更新,它是实现各种效果的关键。在OpenGL中,写Stencil Buffer的开启与否是通过函数glStencilMask(GLuint mask)设置的,这个函数的参数mask对应Stencil值的各个bit是否允许写入,当mask设为0表示完全关闭写Stencil Buffer。在开启写Stencil Buffer的情况下,无论像素是否被Stencil Test或Depth Test剔除,GPU都会执行Stencil值的更新。更新方式是跟Stencil Test和Depth Test的测试结果紧密联系的,OpenGL/D3D把测试结果分为三种情况:sfail: Stencil Test faildpfail: Stencil Test pass但Depth Test faildppass: Stencil Test pass且Depth Test pass通过API glStencilOp(GLenum sfail, GLenum dpfail, GLenum dppass)可以分别为这三种测试结果指定更新该像素Stencil数值的方式,可选的方式包括ActionDescriptionGL_KEEP当前的Stencil值保持不变GL_ZERO将Stencil值更新为0.GL_REPLACE将Stencil值替换为参考值GL_INCR若当前Stencil值小于最大值,则加1GL_INCR_WRAPStencil值加1,若超过最大值则wrap为0GL_DECR若当前Stencil值大于最小值,则减1GL_DECR_WRAPStencil值减1,若小于0则wrap为最大值GL_INVERT按位反转当前Stencil值GPU在执行Stencil Test和Depth Test(没有Enable Depth Test的话将一直pass),按照测试结果(sfail,dpfail,dppass)对应的方式算出新的Stencil值,如有发生变化则写回Stencil Buffer里。 正是有上面的多种更新方式,以及Depth Test和Stencil Test的紧密联系使得Stencil Test能通过多个pass实现多种效果。2. Stencil Test的应用从上面可以看出,Stencil应用的过程大概是这样:开启写Stencil Buffer渲染物体,更新Stencil Buffer的内容关闭写Stencil Buffer渲染(其他)物体,通过Stencil Buffer的内容把部分像素剔除掉。我们看下不同的更新机制如何实现特定需求的。2.1 轮廓给物体添加轮廓的思路很简单——把同一个物体画两遍,其中第一遍正常地渲染物体,第二遍将原物体做微小拉伸(比原来多出轮廓),并让Pixel Shader输出轮廓颜色。同时要使第一遍所画的像素位置上在第二遍渲染中不会再被画出新的像素,即需要使用一种剔除方法,使第二次渲染时只保留两次渲染物体的非重叠部分。一开始我们可能会想到用Depth Test——第一次渲染时打开Depth Write,在第二遍渲染时在Vertex Shader给构成网格的每个顶点设一个足够大的深度值,这样第二次渲染时重叠部分会在GPU的Depth Test中因为遮挡而被剔除。然而,当场景里存在其他背景物体时,轮廓也会被遮挡住。因此,Depth Test并不是过滤像素区域的好方法,而这样的需求场景,本来就是Stencil Test的舞台。利用Stencil Test画轮廓的大概步骤是这样的:1)将sfail, dfail, dpass的更新方式分别设为KEEP,KEEP,REPLACE glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);2)关闭写Stencil Buffer,按正常方式渲染背景。 glStencilMask(0x00); //draw the background …3)开启写Stencil Buffer,比较函数为ALWAYS,Stencil Test参考值设为1。渲染物体,这样渲染后物体每个像素的Stencil值将等于1 glStencilFunc(GL_ALWAYS, 1, 0xFF); glStencilMask(0xFF); //draw the object …4)关闭写Stencil Buffer,比较函数设为NOTEQUAL,关闭Depth Test。将物体做微小拉伸并渲染物体,Pixel Shader输出轮廓颜色 glStencilFunc(GL_NOTEQUAL, 1, 0xFF); glStencilMask(0x00); glDisable(GL_DEPTH_TEST); //draw the scaled object …图 2 轮廓渲染这个方法的思想很简单:第一次渲染物体后,最终所有画出的像素对应的Stencil值均为1,而第二次渲染时只画出Stencil值不等于1的轮廓,从而实现了期望的效果。图2是用learnopengl教程在Stencil这一章中画出的例子,个人觉得这个网站的教程很适合初学OpenGL,里面对第三方库怎样build和使用有详细的解释,并且从最基本的例子开始展开循序渐进,最重要的是每个例子都有代码可参考。2.2 Dissolve在Graphics或Video领域,Dissolve用于描述一种过渡效果——一张图片渐渐地褪去,在同时另一张图片替换原来的图片。Dissolve可使用Stencil Buffer实现,在一开始将Stencil Buffer清零,通过设置不同的比较函数,使第一张图片全部画出,而第二张图片全部不画。接着逐帧改变Stencil Buffer,逐渐增加1的个数,并以同样的方式画两张图片,直到最后Stencil Buffer全为1,只画出了第二张图片的所有像素。实现Dissolve的其中一帧的过程大概是1) 开启Stencil,并将stencil比较函数设为GL_NEVER,参考值设为1,将sfail的更新方式设为GL_REPLACE, glStencilFunc(GL_NEVER, 1, 1) glStencilOp(GL_REPLACE, GL_KEEP, GL_KEEP)2)通过画几何体或glDrawPixels函数往Stencil Buffer里写入特定的Dissolve样式,由于Stencil Test一直fail,所有这些像素不会被画出 3)关闭写Stencil Buffer,将比较函数设为GL_EQUAL,参考值设为0,并画第一张图片,这样只有模板上为0值的地方才画出这张图片的像素 glStencilFuncGL_EQUAL, 0, 1(GL_EQUAL, 0, 1). //draw the 1st image …4)改变比较的参考值为1,并画第二张图片 glStencilFuncGL_EQUAL, 1, 1(GL_EQUAL, 1, 1). //draw the 2nd image …2.3 Shadow Volume以上的更新机制比较简单,这里我们继续看一个相对较复杂的应用——Shadow Volume,Shadow Volume最早是Frank Crow于1977年提出的一种为3D场景添加阴影的算法,后来也有其他研究者独立地提出一些变种算法。The Theory of Stencil Shadow Volumes给出了Shadow Volume的详细介绍。Shadow Volume算法旨在光栅化的渲染中,确认出所渲染物体上那些受遮挡影响未能被光源照到像素,生成一个模板,然后剔除对应的像素不做lighting,从而实现阴影效果。该算法的第一步是构造一个Shadow Volume(这里不是指算法名字了,而是一个图3那样的Volume),其基本步骤是以光源为视点,找出遮挡物的所有轮廓边(那些同时被正面三角形和反面三角形包含的边)将轮廓边上的每一点向光源与其连线的方向延伸,所有边构成的多边形形成一个立体(即Shadow Volume,图3的阴影部分)的四周表面。另外可能要加上Front Cap或Back Cop,从而形成封闭的Shadow Volume。加何种Cap因不同算法而异。图3 遮挡物在光源的延伸方向上形成的Shadow Volume在构造Shadow Volume完成后,渲染过程大概如下:按无光照渲染整个场景,即所有物体都出于阴影中对于每个光源,执行以下步骤:渲染构造好的Volume,利用深度信息构造出一个模板,使出于光照中的像素在模板上有不同的Stencil值按有光照渲染整个场景,利用步骤1构造的模板区分阴影区域,使用额外的Blending把渲染结果添加到已有场景中按照构造模板方法分类,Shadow Volume算法可分为两类Depth passDepth failDepth pass和Depth fail分别在dppass和dpfail两种测试结果更新Stencil值。Wiki里还提到Exclusive-or的方法,这种方法也是在Depth pass时更新Stencil值,但它只采用了1bit的Stencil值,更新方式为INVERT,因此并不适用于有多个Shadow Volume重叠的情况。下面着重看戏这两种方法对于Stencil Buffer的使用,对两者的优缺点暂不做讨论。2.3.1 Depth passDepth pass的思路是分两次分别渲染Shadow Volume的正面和反面,并用Stencil值记录位于物体前方的次数。如果正面和反面的次数相等,那么该位置出于光照中。如果正面的次数比反面多,那么该位置出于阴影中。因为Stencil值是在通过depth测试时更新的,所以这种方法较Depth pass。Depth pass构造应用模板的步骤为:关闭写Depth Buffer和Color Buffer,设置back-face culling,将dppass的更新方式设为GL_INCR.渲染Shadow Volume,由于Culling,只画了Shadow Volume的正面.设置front-face culling,将dppass的更新方式设为GL_DECR渲染Shadow Volume,由于Culling.只画了Shadow Volume的反面图4 Depth pass Shadow volume如图4,箭头末端的数字分别对应每个位置经过以上步骤后最终在Stencil Buffer里的数值,可以看到,出于阴影中的位置最终为1,因为它出于Shadow Volume的正面和反面之间,正面未被物体遮住depth pass之后Stencil值增1,而反面被物体遮住depth测试失败未能将Stencil值减1。当一个位置与眼睛的连线未闯过Shadow Volume(从左到右的第1条连线)或者穿过正反面(第2和第4条连线),那么意味着该位置在光照中。2.3.2 Depth fail另一种方法Depth fail通过在dpfail时更新Stencil值来构造模板,Depth fail的步骤为:关闭写Depth Buffer和Color Buffer,设置front-face culling,将dpfail的更新方式设为GL_INCR.渲染Shadow Volume,由于Culling,只画了Shadow Volume的正面.设置back-face culling,将dpfail的更新方式设为GL_DECR渲染Shadow Volume,由于Culling.只画了Shadow Volume的反面Depth fail其实是depth pass的一个“翻转版本”——depth pass算出正面和反面在物体前方的次数,而depth fail则算反面和正面在物体后方的次数。这种差异导致了两者在实际应用中有各自的优势和不足,这些超出本文范围,就不深入了。这里是一个提供代码的depth pass例子:Shadow Volume。2.3.3 Two-Sided Stencil以上Shadow Volume的正反面是分两次渲染的,这无疑增加了Vertex Shader的带宽。事实上可以利用Two-Sided Stencil功能,对于OpenGL可通过下面两个函数分别为Front和Back设置不同的更新方式,那么整个Shadow Volume实际上只需要画一次,同时画正面和背面,由GPU根据三角形的Face去选择更新Stencil值的方式。 void glStencilFuncSeparate(GLenum face, GLenum func, GLint ref, GLuint mask); void glStencilOpSeparate(GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass);2.3.4 总结Shadow Volume算法是将Stencil Buffer的数值当做计数器来使用,用于统计物体每个位置的正面和反面的数量,以之判断物体与Shadow Volume的关系。本质上,Stencil Buffer使用来记录物体与Shadow Volume两个面的遮挡关系,这也解释了Stencil值的更新为什么要跟Depth Test的结果绑定在一起。2.4 其他除了上述提到的应用外,Wiki中提到的Stencil Test其他应用还有Decaling,portal rendering,Reflections,intersection highlighting等,留待慢慢消化。

March 9, 2019 · 2 min · jiezi