GLSL和深度测试解释

OpenGL ES 2.0 着色器基础

depth testing(深度测试)的功能,启动它,OpenGL就可以跟踪在z轴上的像素。这样它只会在那个像素前方没有东西时,才会绘画这个像素。

///////////////////////////////////////////////////////////////////////////////////////////////////////////////

限定符:

attribute表示修饰顶点属性的变量,这种类型的变量只能出现在vertex shader中,且为只读性性。

uniform表示修饰图元,帧的变量,这种类型的变量无论在vertex shader还是fragment shader中都是只读属性,不可写。

varying表示是可读可写的变量,在vertex shader只能写,在fragment shader中只能读,用于vertex shader与fragment shader传数据,并插值。

const是常量限定符,与c/c++中类似


GLSL语法跟C语言非常相似:
1.数据类型:
GLSL包含下面几种简单的数据类型
float 
bool :false or ture
int
向量:
vec   {2,3,4}     长度为2, 3, 4的float向量
bvec {2,3,4}     长度为2, 3, 4的bool向量
ivec  {2,3,4}     长度为2, 3, 4的int向量
矩阵:
mat2   2*2的浮点矩阵
mat3   3*3的浮点矩阵
mat4   4*4的浮点矩阵
以上三种矩阵可以简写为mat2 mat3 mat4
矩阵的行和列并没有规定相等,因为可以使用mat2*3 mat 4*2等方法来声明行数和列数
一种特殊的数据类型:取样器--用于纹理采样
sampler1D     访问一个一维纹理
sampler2D     访问一个二维纹理
sampler3D     访问一个三维纹理
samplerCube  访问一个立方体纹理
sampler1DShadow 访问一个带对比的一维深度纹理
sampler2DShadow 访问一个带对比的二维深度纹理

GLSL提供了类似C语言的用户定义结构:
struct dirlight{
          vec3 direction;
          vec3 color;
};

变量限定符:
限定符赋给变量特殊的含义:
const--     用于声明非可写的编译时常量变量
attribute-- 用于经常更改的信息,只可以再顶点着色器中使用
uniform--  用于不经常更改的信息,用于顶点着色器和片元着色器
varying--   用于慈宁宫顶点着色器传递到片元着色器的插值信息

控制流:
GLSL的控制流  与C++非常类似,可以使用for while以及do-while实现循环,也可以使用if和if-else进行选择,不过if语句中的变量声明,只是在最近的硬件中才提供

函数:
GLSL也提供了一些特殊的实现:
continue
break
discard --只可用于片元着色器,当控制流遇到这个关键字时,正在处理的片元就会被标记为将要丢弃
函数
main() 可以返回除了数组外的任何类型
对于函数的参数 可以使用下面几种限定符
in -- 复制进函数但不在返回时复制,在函数内部仍然是可写的
out--只在返回时复制,是可读的
inout  复制进函数并在返回时复制
如果没有指定限定符,默认情况下为in
函数可以通过参数类型重载,但是不能仅仅通过返回类型重载,同样,因为不会执行参数类型自动提升,所以调用函数时参数类型必须完全匹配
函数不能被递归调用

GLSL Vertex shader内置的输入变量,注意这些变量都是不可更改的
attribute vec4  gl_Color;                               顶点数据字段的Diffuse颜色
attribute vec4  gl_SecondaryColor;                顶点数据字段的Specular颜色
attribute vec4  gl_Normal;                             顶点法线
attribute vec4  gl_Vertex;                             顶点位置
attribute vec4  gl_MultiTexCoord0;                8组贴图坐标
attribute vec4  gl_MultiTexCoord1;
attribute vec4  gl_MultiTexCoord2;
attribute vec4  gl_MultiTexCoord3;
attribute vec4  gl_MultiTexCoord4;
attribute vec4  gl_MultiTexCoord5;
attribute vec4  gl_MultiTexCoord6;
attribute vec4  gl_MultiTexCoord7;
attribute vec4  gl_MultiTexCoord0;
attribute vec4  gl_MultiTexCoord1;
attribute vec4  gl_FogCoord;                         使用雾效果的参考数值
在编写shader时,可以把这些输入数据所代表的功能重新定义,名称只是用来让传入数据时有个规则可循而已,C++调用glVertexPointer所指到的vetex buffer数据,在GLSL中可以通过gl_Vertex变量来获得。

Vertex Shader的输出数据时使用的内置变量:
vec4 gl_posotion;                用来设置顶点转换到屏幕坐标的位置,Vertex Shader一定要去更新这个数值
float gl_pointSize;                是启动PointSprite功能时,用来设置矩形大小的数值
vec4 gl_ClipVertex;              如果启用了Clip Plane功能,gl_ClipVertex可以放入用来与Clip Plane平面做测试用的位置

下面的输出数据在Vertex Shader中用来输出数据,在Fragment Sahder也可以使用这些变量,但是是用来读取数据:
araying Vec4   gl_FrontColor;                          对正面做不同的光照计算 ,这两组颜色分主要颜色和次要颜色 代表的是固管的Diffuse值
varying vec4   gl_BackColor;                            背面
varying vec4   gl_FrontSecondDaryColor;          固管的Specular值 
varying vec4   gl_BackSecondaryColor;
varying vec4   gl_TexCoord[gl_MaxTextureCoords]; glTextCoord[0]是指第0个贴图坐标  
varying vec4   gl_FogFragCoord;
Fragment Sahder除了可以从上面几个所列出的变量获得内插结果外,还可以从另外两个内置变量得到一些无法从Vertex Shader获得的数值
vec4 gl_FragCoord;  gl_FragCoorg.xy代表像素在Framebuffer画面的位置,gl_FragCoord.z代表这个店在做Z Buffer测试时所用的Z值
bool gl_FrontFacing; 可用来查询目前正在画的像素是来自三角形的正面还是来自他的背面

Fragment Shader的内置输出变量:
vec4 gl_FragColor;                               代表画面所要填入的颜色
vec4 gl_FragData[gl_MaxDrawBuffers];  用来填入画面的颜色,用在启用多个FrameBuffer时,调用gl_FragData填入画面颜色
vec4 gl_FragDepth;                             用来指定Z Buffer测试时所使用的Z值,这样就可以不通过顶点内插得到的Z值

对于Vertex Shader来说,除了可通过内置变量来内插数值给Fragment Shader之外,也可以不通过内置变量,只要在Vertex Shader和Fragment Shader中声明相同名称的全局变量,GLSL就可以自动的把这两个数值连接起来

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

在OpenGL ES2.0 的世界,在场景中渲染任何一种几何图形,你都需要创建两个称之为“着色器”的小程序。

着色器由一个类似C的语言编写- GLSL
这个世界有两种着色器(Shader):
  •   Vertex shaders – 在你的场景中,每个顶点都需要调用的程序,称为“顶点着色器”。假如你在渲染一个简单的场景:一个长方形,每个角只有一个顶点。于是vertex shader 会被调用四次。它负责执行:诸如灯光、几何变换等等的计算。得出最终的顶点位置后,为下面的片段着色器提供必须的数据。
  •   Fragment shaders – 在你的场景中,大概每个像素都会调用的程序,称为“片段着色器”。在一个简单的场景,也是刚刚说到的长方形。这个长方形所覆盖到的每一个像素,都会调用一次fragment shader。片段着色器的责任是计算灯光,以及更重要的是计算出每个像素的最终颜色。

  下面我们通过简单的例子来说明。

代码:

attribute vec4 Position; // 1
attribute vec4 SourceColor; // 2
 
varying vec4 DestinationColor; // 3
 
void main(void) { // 4
    DestinationColor = SourceColor; // 5
    gl_Position = Position; // 6
}

解析:

  1. “attribute”声明了这个shader会接受一个传入变量,这个变量名为“Position”。在后面的代码中,你会用它来传入顶点的位置数据。这个变量的类型是 “vec4”,表示这是一个由4部分组成的矢量。
  2. 与上面同理,这里是传入顶点的颜色变量。
  3. 这个变量没有“attribute”的关键字。表明它是一个传出变量,它就是会传入片段着色器的参数。“varying”关键字表示,依据顶点的颜色,平滑计算出顶点之间每个像素的颜色。文字比较难懂,我们一图胜千言:图中的一个像素,它位于红色和绿色的顶点之间,准确地说,这是一个距离上面顶点55/100,距离下面顶点45/100的点。所以通过过渡,能确定这个像素的颜色.设置目标颜色 = 传入变量:SourceColor
  4. 设置目标颜色 = 传入变量:SourceColor
  5. gl_Position 是一个内建的传出变量。这是一个在 vertex shader中必须设置的变量。这里我们直接把 gl_Position = Position; 没有做任何逻辑运算。

一个简单的vertex shader 就是这样了,接下来我们再创建一个简单的fragment shader。
代码:

varying lowp vec4 DestinationColor; // 1
 
void main(void) { // 2
    gl_FragColor = DestinationColor; // 3
}
下面解析:
  1. 这是从vertex shader中传入的变量,这里和vertex shader定义的一致。而额外加了一个关键字: lowp。在fragment shader中,必须给出一个计算的精度。出于性能考虑,总使用最低精度是一个好习惯。这里就是设置成最低的精度。如果你需要,也可以设置成medp或者highp.
  2. 正如你在vertex shader中必须设置gl_Position, 在fragment shader中必须设置gl_FragColor.这里也是直接从 vertex shader中取值,先不做任何改变。
在 Cesium 中使用 `PointPrimitive` 时,如果你希望点能够**正确地被地形遮挡**(即:点在地形下方时不显示),可以使用 **自定义着色器(CustomShader)** 来实现深度测试(Depth Test)。 从 Cesium **1.99 版本起**,`PointPrimitive` 支持通过 `customShader` 属性自定义渲染逻辑,包括写入深度缓冲(`gl_FragDepth`),从而实现点与地形之间的正确遮挡关系。 --- ## ✅ 实现步骤 ### 1. 创建 Viewer PointPrimitiveCollection ```javascript const viewer = new Cesium.Viewer("cesiumContainer"); const pointCollection = new Cesium.PointPrimitiveCollection(); viewer.scene.primitives.add(pointCollection); ``` ### 2. 添加点并使用 `CustomShader` 实现深度测试 ```javascript pointCollection.add({ position: Cesium.Cartesian3.fromDegrees(-115.0, 37.0, 0), // 带高度的点 color: Cesium.Color.RED, pixelSize: 10, customShader: new Cesium.CustomShader({ // 自定义片段着色器 fragmentShaderText: ` in vec3 v_positionEC; out vec4 fragColor; void main() { // 计算当前点在相机空间的深度值 float depth = czm_computeEyeDepth(v_positionEC); // 将该深度值写入深度缓冲 gl_FragDepth = depth; // 设置点的颜色 fragColor = vec4(1.0, 0.0, 0.0, 1.0); // 红色 } ` }) }); ``` --- ## ✅ 解释代码 ### 🧩 `czm_computeEyeDepth(v_positionEC)` - 这是一个内置的 Cesium GLSL 函数,用于计算当前点在**相机空间**中的深度值; - `v_positionEC` 是顶点在**Eye Coordinates(相机坐标)**中的位置; - 通过这个函数可以获取当前点的“正确”深度值。 ### 🧩 `gl_FragDepth = depth;` - 将计算出的深度值写入 OpenGL 的深度缓冲区; - 这样,GPU 在渲染时就能判断该点是否被地形遮挡; - 如果地形的深度值更小(即更靠近相机),该点就不会被绘制。 ### 🧩 `fragColor` - 设置该点的颜色输出; - 你可以根据需要设置为动态颜色或透明度。 --- ## ✅ 验证是否支持遮挡 你可以尝试将点的高度设为低于地形的高度,例如: ```javascript position: Cesium.Cartesian3.fromDegrees(-115.0, 37.0, -100) // 地形下方 ``` 如果配置正确,此时该点将被地形遮挡,**不会显示在画面上**。 --- ## ✅ 注意事项 | 事项 | 说明 | |------|------| | Cesium 版本 | 需要 **1.99 或以上**,`CustomShader` 才支持 `PointPrimitive` | | 性能影响 | 自定义着色器会略微增加 GPU 负担,但相比 `Entity` 仍更高效 | | 点交互 | `PointPrimitive` 不支持点击事件,如需交互请使用 `Entity` | | 透明度 | 若使用 `withAlpha`,需要确保 `gl_FragDepth` 正确处理 | | 批量处理 | 可用于百万级点渲染,适合性能敏感场景 | --- ## ✅ 完整示例代码 ```html <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <script src="https://cesium.com/downloads/cesiumjs/releases/1.109/Build/Cesium/Cesium.js"></script> <link href="https://cesium.com/downloads/cesiumjs/releases/1.109/Build/Cesium/Widgets/widgets.css" rel="stylesheet"> </head> <body> <div id="cesiumContainer"></div> <script> Cesium.Ion.defaultAccessToken = 'YOUR_ION_TOKEN'; const viewer = new Cesium.Viewer("cesiumContainer"); const pointCollection = new Cesium.PointPrimitiveCollection(); viewer.scene.primitives.add(pointCollection); pointCollection.add({ position: Cesium.Cartesian3.fromDegrees(-115.0, 37.0, 0), color: Cesium.Color.RED, pixelSize: 10, customShader: new Cesium.CustomShader({ fragmentShaderText: ` in vec3 v_positionEC; out vec4 fragColor; void main() { float depth = czm_computeEyeDepth(v_positionEC); gl_FragDepth = depth; fragColor = vec4(1.0, 0.0, 0.0, 1.0); } ` }) }); </script> </body> </html> ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值