glsl gl_FragCoord 与 屏幕关系



转载链接:  http://blog.csdn.net/jinghouxiang/article/details/50751125


原创  2016年02月26日 17:06:01

[plain]  view plain  
运行环境:Android
opengl es版本: 2.0
3D引擎库 : Rajawali3D


ShaderToy上用的shader语言 为glsl , 效果是用webgl跑的,而webgl封装了opengl es,  所以ShaderToy上的例子同样使用于Android端。

ShaderToy基本上 都是用fragment shader 对栅格化后的像素进行处理。  大部分会用到纹理来丰富最终渲染结果的形状和质地,有些也会用到声音,键盘等外部输入信息。    但是,最终都是归结为对栅格化区域内的每个像素进行处理,因此,我们首先来认识gl_FragCoord这个内置变量以及其与屏幕坐标的关系。

 gl_FragCoord根据glsl language spec的解释为:
   它是fragment shaders的输入变量,并持有该framgent的屏幕相对坐标(x, y, z, 1/w)

  什么是屏幕相对坐标,它的坐标范围是多少,这些都没有告诉我们。需要我们实验
     

  假设我们采用2d ortho projection的方式来渲染,输入的顶点信息为屏幕的4个顶点坐标,这样我们最终渲染出来的是一个 铺满屏幕的图。
 由于是2D渲染,最终每个fragment的 gl_FragCoord的z接近0.0, 而w 为1.0, 而它的x, y分量,是相对于屏幕左下角为原点的屏幕坐标。什么意思?
   假如,我们设定的viewport的渲染区域为(0, 0, 1280, 574) 这么大,那么,gl_Fragment的x分量 范围就在0~1280之间, y分量就在0~574之间。

  我们可以用一下测试用例来进行测试:
    vertex shader:
  
[plain]  view plain  copy
  1. precision mediump float ;  
  2.   
  3.   uniform mat4 uMVPMatrix;  
  4.   attribute vec4 aPosition;  
  5.   
  6.   void main(){  
  7.       position = vec3 (uMVPMatrix*aPosition);  
  8.       gl_Position = uMVPMatrix*aPosition;  
  9.   }  


 
  fragment shader:

   
[plain]  view plain  copy
  1. precision mediump float ;  
  2.     
  3.   uniform vec2 screenSize;  // step  1  
  4.   
  5.   void main()  
  6.   {  
  7.      vec2 uv = vec2(gl_FragCoord.xy/screenSize.xy);  // step 2  
  8.   
  9.     //Calculate polar coordinates  
  10.      float r = length(uv);  
  11.      float c = 0.0;  
  12.   
  13.     if(uv.x>0.98 &&uv.x<1.0 )                       // step 3  
  14.     {  
  15.        c = 1.0;  
  16.     }  
  17.   
  18.      if(uv.y>0.98 &&uv.y<1.0 )                     // step 4  
  19.      {  
  20.        c = 1.0;  
  21.      }  
  22.   
  23.      //Calculate the final color mixing lines and backgrounds.  
  24.      vec3 bg = mix(vec3(0.93 , 0.71 ,  0.62 ),  vec3(0.9 , 0.44 , 0.44),   r);  // step 5  
  25.      bg = mix(bg, vec3 (0.9 , 0.91 , 0.62 ), c);                                //  step 6  
  26.   
  27.      gl_FragColor = vec4(bg, 1.0);  
  28. }  

运行后,效果图如下:
  

这里主要说下 fragment shader:
   step 1: 之前说到, gl_FragCoord的坐标范围, 这里screenSize 表示屏幕的宽高。
   step 2 :   将每个fragment的 每个gl_FragCoord归一化,这是一个惯例,利于后面计算
   step3: 和 step4:  这两个分别表示,当fragment的坐标(x, y) 的x和y分量分别落在这个范围时(即中间)c 的值会发生变化
   step5:  step5 和 step6都用到了glsl 的 mix内置函数,  考虑到 bg = mix(color1, color2, r)它的意思,就是将color1和color2 两种颜色,按照bg = color1*(1-r)+r*color2的方式混合,其中bg, color1, color2都是表示颜色,有三个分量。 那么,当r=0.0时,表示的是color1颜色,当r = 1.0表示的是color2颜色。 我们利用mix可以在一种背景上标记出另一种颜色。

 step3 和 step4 表示,当x, y 分别落在值范围的中间时,c的值从0.0变为1.0, 即在这个范围内,背景色变为我们设置的颜色。


当我们把step3 和 step4 改为:
      if  (uv.x> 0.09  &&uv.x< 0.11  )                       // step 3
    {
       c = 1.0;
    }

     
if  (uv.y> 0.09  &&uv.y< 0.11  )                     // step 4
     {
       c = 1.0;
     }

效果图如下:


通过,以上的例子我们得出gl_FragCoord的正确表示方式。

要实现环形波纹效果,可以使用OpenGL的着色器语言GLSL来编写着色器程序。具体的实现步骤如下: 1. 定义顶点着色器程序,将顶点坐标传递给片段着色器。 ``` #version 330 core layout (location = 0) in vec3 aPos; void main() { gl_Position = vec4(aPos, 1.0); } ``` 2. 定义片段着色器程序,根据顶点坐标计算出每个像素点的颜色值。 ``` #version 330 core out vec4 FragColor; uniform float time; uniform vec2 center; // 波纹中心 uniform float radius; // 波纹半径 uniform float strength; // 波纹强度 void main() { vec2 uv = gl_FragCoord.xy / vec2(800, 600); // 屏幕坐标系转换到纹理坐标系 float dist = length(uv - center); if (dist < radius) { float alpha = (radius - dist) * strength; float offset = time * 2.0; float angle = atan(uv.y - center.y, uv.x - center.x) + offset; uv.x += alpha * cos(angle); uv.y += alpha * sin(angle); } FragColor = texture(myTexture, uv); } ``` 在片段着色器中,我们定义了一些常量和变量来控制波纹的中心、半径和强度。根据像素点到波纹中心的距离,可以计算出波纹的强度,然后通过一个偏移量来控制波纹的运动方向和速度。最后,将波纹的偏移量应用到纹理坐标上,从而实现波纹效果。 3. 在主程序中,创建一个帧缓冲对象,并将片段着色器渲染的结果绘制到屏幕上。 ``` // 创建帧缓冲对象 unsigned int framebuffer; glGenFramebuffers(1, &framebuffer); glBindFramebuffer(GL_FRAMEBUFFER, framebuffer); // 创建纹理附件 unsigned int texture; glGenTextures(1, &texture); glBindTexture(GL_TEXTURE_2D, texture); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 800, 600, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0); // 检查帧缓冲是否完整 if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) std::cout << "ERROR::FRAMEBUFFER:: Framebuffer is not complete!" << std::endl; // 渲染循环 while (!glfwWindowShouldClose(window)) { float time = glfwGetTime(); // 渲染到帧缓冲 glBindFramebuffer(GL_FRAMEBUFFER, framebuffer); glViewport(0, 0, 800, 600); glClear(GL_COLOR_BUFFER_BIT); shader.use(); shader.setFloat("time", time); shader.setVec2("center", vec2(0.5, 0.5)); shader.setFloat("radius", 0.3); shader.setFloat("strength", 0.1); glBindVertexArray(VAO); glDrawArrays(GL_TRIANGLES, 0, 6); // 渲染到屏幕 glBindFramebuffer(GL_FRAMEBUFFER, 0); glViewport(0, 0, 800, 600); glClear(GL_COLOR_BUFFER_BIT); screenShader.use(); glBindVertexArray(screenVAO); glBindTexture(GL_TEXTURE_2D, texture); glDrawArrays(GL_TRIANGLES, 0, 6); glfwSwapBuffers(window); glfwPollEvents(); } ``` 在主程序中,我们创建了一个帧缓冲对象,并将片段着色器渲染的结果绘制到帧缓冲对象中。然后再将帧缓冲对象中的纹理绘制到屏幕上。这样就可以实现环形波纹效果了。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值