« 水效果Ⅱ - 涟漪学一学,VBO »

Vertex Texture Fetch 顶点纹理拾取

 Vertex Texture Fetch,简称VTF,是Shader Model的一个特性。其本质没有什么复杂的:就是在顶点Shader里检索纹理。——ZwqXin.com

[Shader快速复习:Per Pixel Lighting(逐像素光照)]
[Shader快速复习:Cube Mapping(立方环境贴图)]
[Shader快速复习:Reflection And Refraction(反射与折射)]

本文来源于 ZwqXin (http://www.zwqxin.com/), 转载请注明
      原文地址:http://www.zwqxin.com/archives/shaderglsl/glsl-vertex-texture-fetch.html

是的,在vertex shader里也可以检索纹理。我本来觉得这没什么好奇怪的,因为我一直也觉得这很当然可以啊~当初橙书(OpenGL Shading Language Edtion2)也说过texture2D这类函数不是fragment shader专用的,倒还有texture2DLod这种在vertex shader里专用的(后面一句是马后炮~),只是我不知道怎么用,在哪里用,以及更重要的:为什么要用。

为什么要在vertex shader里检索纹理。

都知道,纹理里的一般是一幅图像,无论是外部导入的还是通过FBO等手段渲染到的。既然如此,有意义的当然是图像里的每一个像素啦,通过纹理坐标检索纹理中的像素打印到屏幕某个地方,并可控制细节程度……反映在GPU编程中,一般就是把当前绑定的纹理的纹理单元(默认为0)传送给Fragment Shader作为sampler,在vertex shader里用gl_TexCoord[0] = glMutiTexCoord0这样的语句,获取固定流水线中为每个顶点设置好的纹理坐标(顶点纹理索引,即glMutiTexCoord0),赋给本质为varying的gl_TexCoord[0],让它带着纹理坐标在光栅化过程中插值——对应每个像素点拥有属于它的插值后像素纹理索引(gl_TexCoord[0]),以此作为参数用texture2D类函数检索纹理sampler。

直接在顶点阶段就检索纹理意义何在?获得的只是那些顶点的纹理坐标检索出的“孤立”像素值而已。

你认识吗?GPGPU。

[gpgpu.org]

GPGPU(General Purpose Graphic Process Unit,通用目的图形处理单元),是应用GPU的高速并行能力和浮点运算能力进行科学计算等SIMD类型[单指令多数据]的应用。在这里,GPU-shader不仅仅着眼于图形。而GPGPU的一个重要概念就是:纹理 =  数组。是的,为什么不可以呢?纹理确实就是数组啊。我们能传入shader的只有具体的数值,bool,int,float,vec,matrix,其中最大的matrix4也只有16个量。那么如果我们要把大量的数据传入shader,譬如一个巨大的float数组,怎么办呢?对啊,用纹理!这时候,纹理内部每个数值不再是像素的值,而是数组的数据项。我们只是通过纹理这种灵活的媒介,让数据“进入”GPU的视野,让shader可以对这些数据项变量进行访问和操作。

顺带一提,现在科学计算领域已经进入GPGPU的进化时代 -CUDA时代了。好吧,不要扯远了。

既然纹理 = 数组, VTF顶点纹理拾取的存在就不言自明了:其实不是在拾取含有图像像素信息的那个纹理,而是在拾取含有顶点数据信息的那个“数组”啊!在这里,数组的索引就是顶点纹理坐标……看例子:

  1. //RenderMonkey:
  2. //Vertex Program
  3. varying vec4 vertColor;
  4. uniform sampler2D baseMap;
  5.  
  6. void main( void )
  7. {
  8.     //vertColor = texture2D(baseMap, gl_MultiTexCoord0.xy);  与下句等价  
  9.     vertColor = texture2DLod(baseMap, gl_MultiTexCoord0.xy, 0.0);        
  10.     vec4 pos = gl_ModelViewMatrix * (gl_Vertex) ;
  11.     gl_Position = gl_ProjectionMatrix * pos ; 
  12. }
  13.  
  14. //Fragment Program
  15. varying vec4 vertColor;
  16.  
  17. void main( void )
  18. {
  19.     gl_FragColor = vertColor;
  20. }

这个例子是说明:诶?原来Vertex Shader里也可以做纹理拾取口牙!顺带一提,这里用texture2D,和用“texture2DLod+尾参数[细节参数LOD] = 0.0”的效果是一样的:
 

www.zwqxin.com  VTF 顶点纹理拾取
(对比用。这是FTF,传统的fragment shader获取纹理)
www.zwqxin.com  VTF 顶点纹理拾取
(这是VTF,顶点纹理拾取,也就是上面代码的产物,对比两图哈)

纹理只是普通的纹理。只是为了证明VTF能行- -。把顶点纹理的值做插值,预料最后的结果类似于顶点颜色插值,三角片元的颜色在三角的三顶点所获得的纹理颜色间进行线性插值,得出如此“重过渡味+模糊”的怪象(嘛~这纹理即使是那FTF出来的也是怪象)。然后测试texture2DLod这个函数,把最后的LOD参数增大调为0.4:

www.zwqxin.com  VTF 顶点纹理拾取

lod是细节参数,这跟以前的FTF(PTF)差不多。好吧,换张纹理后,再用VTF做点更有趣的:

  1. //RenderMonkey: 
  2. //Vertex Program 
  3. varying vec4 vertColor; 
  4. uniform sampler2D baseMap; 
  5.  
  6. void main( void ) 
  7.      vertColor = texture2DLod(baseMap, gl_MultiTexCoord0.xy, 0.0);
  8.     vec4 offset = vec4(0.0);
  9.     if(gl_Vertex.z > 0.0)
  10.           offset = vertColor;
  11.     else if(gl_Vertex.z < 0.0)
  12.            offset = vec4(1.0)-vertColor;
  13.            
  14.     vec4 pos = gl_ModelViewMatrix * (gl_Vertex+ offset) ; 
  15.     gl_Position = gl_ProjectionMatrix * pos ; 
  16.  
  17. //Fragment Program 
  18. varying vec4 vertColor; 
  19.  
  20. void main( void ) 
  21.     gl_FragColor = vertColor; 
  22. }

能猜到结果变成这样吗?哈,VTF出来的vertColor果然充满力量:

www.zwqxin.com  VTF 顶点纹理拾取

另外一个例子则用VTF做点有意义的事情。还记得高度图纹理吗?(我在[Terrain Texture-Array Demo] 里也用到过~)

  1. //RenderMonkey:  
  2. //Vertex Program  
  3. varying vec2  texCoord;
  4. uniform sampler2D Texture0;
  5.  
  6. void main(void)
  7. {
  8.    vec4 vcol =  texture2D( Texture0, gl_MultiTexCoord0.xy);
  9.     
  10.    float gray = 0.2990*vcol.r + 0.5870*vcol.g + 0.1140*vcol.b;
  11.  
  12.    vec4 pos =  gl_Vertex;
  13.       pos.z = pos.z * (1.0 - 5.0*gray);
  14.  
  15.    gl_Position = gl_ModelViewProjectionMatrix * pos;
  16.    
  17.     texCoord = gl_MultiTexCoord0.xy;   
  18. }
  19.  
  20. //Fragment Program  
  21. uniform sampler2D Texture0;
  22. varying vec2 texCoord;
  23.  
  24. void main(void)
  25. {
  26.     gl_FragColor = texture2D( Texture0, texCoord );
  27. }

这里本来只有一个平整的网格,和一张类似高度图的纹理。运用VTF把顶点对应的纹理坐标的像素值拉出来转化为灰度(转化法同见[基于亮度的图像二值化处理] ),并转化为该网格顶点的“高度”。最后的纹理只是平铺上去(那不是阴影哦)。看,灰度高的地方对应的高度高,灰度低的地方对应的高度低。这就是高度场啊,这就是VTF最典型的应用啊!

www.zwqxin.com  VTF 顶点纹理拾取

在Vertex Shader里面,通过纹理坐标的检取,VTF获取的是真正的“高度值”数据……只是这些数据被储存在一张纹理上罢了。

本文来源于 ZwqXin (http://www.zwqxin.com/), 转载请注明
      原文地址:http://www.zwqxin.com/archives/shaderglsl/glsl-vertex-texture-fetch.html

  • quote 1.XanaHopper
  • http://blog.xanahopper.com
  • 博主……问一下,4.x时代不是verying都被遗弃了么,推荐同意用in和out么……
    zwqxin 于 2013-4-30 8:32:59 回复
    是不是deprecated了不清楚,但是标准上是需要换用in和out的。
  • 2013-4-29 12:40:23 回复该留言
  • quote 2.mingqiangyin
  • http://www.zwqxin.com/archives/shaderglsl/glsl-vertex-texture-fetch.html
  • 您好博主,最近也在学glsl,所以想用glsl来实现纹理的融合,在遇到两张图片纹理,只有相交部分融合时遇到了一些问题,始终没有实现,但是在OpenGL中很好实现,利用glBlendFunc(...),指定两个纹理贴图的位置即可。但是这种方法在shader貌似有点行不通,还请博主指点一二,非常感谢
    zwqxin 于 2014-12-31 21:45:13 回复
    glBlendFunc与glsl不冲突吧。如果只是想混合两张2D图片,方法网上很多可以学习
  • 2014-12-30 12:23:00 回复该留言
  • quote 3.mtyong
  • 博主您好:
    我目前在尝试用opengl es 2.0尝试将一张2D图像3D化,其实也就是根据不同的颜色值设置不同的z坐标。和您的高度纹理这个例子非常的相似。
    目前我的思路是用texture2D导入这张图的所有信息,然后使用您的VTF算法导出3D的结果。
    但是目前我有一个疑问,在这一句:vec4 pos = gl_Vertex;中,使用了gl_Vertex,而opengl es中没有gl_Vertex。请问可以用什么代替呢?另外,最后绘制的时候,需不需要设定顶点绘制顺序呢,十分感谢。
    zwqxin 于 2015-1-23 21:27:08 回复
    gles把position stream buffer传入即可,绘制顺序由index buffer等方式指定,你可以先熟悉一下gles2.0或gl3.0的简单的使用方式(网上搜一下入门的基本使用教程),再实际进行编程。
  • 2015-1-23 9:22:45 回复该留言
  • quote 4.pyf12345
  • 博主您好,我目前正在学习glsl,对高度图纹理这个特别感兴趣,能发个demo给我吗?谢谢!120421563@qq.com
  • 2016-12-26 11:21:40 回复该留言

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

IE下本页面显示有问题?

→点击地址栏右侧【兼容视图】←

日历

Search

网站分类

最新评论及回复

最近发表

Powered By Z-Blog 1.8 Walle Build 100427

Copyright 2008-2013 ZwqXin. All Rights Reserved. Theme edited from ipati.