cocos2dx中用shader实现折射效果
目的:给定任意法线贴图,实现折射效果
主要技术:RenderTarget,glsl
提取出一个可供使用的Sprite派生类
计算模型如下:
黑色部分为玻璃切线,红色部分为场景。绿色线为垂直于场景的视线及其延长线,红色为法线。我们求折射光,等于在给定的OB,法线向量下,求取视线经过折射后与场景的交点和视线延长线和场景交点的偏移量。求出偏移量以后,就能非常方便的利用texture2D函数采样rendertexture,得到颜色。
假设法线向量为a,则我们容易得到x, y方向的偏移为:
deltaX = a.x * OB / a.z
deltaY = a.y * OB / a.z
由于进入fragment shader的坐标系是ndc坐标系,外部需要给shader屏幕尺寸width, height,从而得到:
deltaX(ndc) = a.x * OB / (a.z * width)
deltaY(ndc) = a.y * OB / (a.z * height)
接下来就非常简单
gl_FragColor = texture2D(renderTarget, worldUV + deltaXY(ndc))
得到worldUV的方法:
在vertex shader中:
worldUV = MVP * position
worldUV = worldUV / worldUV.w //这个很重要,必须除了w才能得到ndc坐标,否则结果不对
worldUV = vec2((worldUV.x + 1.) * 0.5, (1. - worldUV.y) * 0.5)
所有shader代码:
attribute vec4 a_position; attribute vec2 a_texCoord; attribute vec4 a_color; #ifdef GL_ES varying mediump vec2 v_texCoord; varying mediump vec2 v_worldTexCoord; varying mediump vec4 v_fragmentColor; #else varying vec2 v_texCoord; varying vec2 v_worldTexCoord; varying vec4 v_fragmentColor; #endif void main() { gl_Position = CC_PMatrix * a_position; v_texCoord = a_texCoord; v_fragmentColor = a_color; v_worldTexCoord = gl_Position.xy / gl_Position.w; v_worldTexCoord = vec2((v_worldTexCoord.x + 1.) * 0.5, (1. - v_worldTexCoord.y) * 0.5); } #ifdef GL_ES precision lowp float; #endif varying vec4 v_fragmentColor; uniform vec2 u_screenExtent; uniform sampler2D sceneTexture; uniform vec3 u_rgbRatio; uniform float u_height; varying vec2 v_texCoord; varying vec2 v_worldTexCoord; void main() { vec2 worldTex = vec2(v_worldTexCoord.x, 1. - v_worldTexCoord.y); vec4 normal = texture2D(CC_Texture0, v_texCoord) * 2. - vec4(1., 1., 1., 0); vec2 bias = vec2(u_height * normal.x / (normal.z * u_screenExtent.x), u_height * normal.y / (normal.z * u_screenExtent.y)); vec4 frag = vec4(0.,0.,0.,0.); frag.r = texture2D(sceneTexture, worldTex + bias * u_rgbRatio.r).r; frag.g = texture2D(sceneTexture, worldTex + bias * u_rgbRatio.g).g; frag.b = texture2D(sceneTexture, worldTex + bias * u_rgbRatio.b).b; gl_FragColor = frag; gl_FragColor.a = 1.; }
贴一个效果图: