700字范文,内容丰富有趣,生活中的好帮手!
700字范文 > shader 反射 水面_2D水面波光效果 以及一些2D常用shader的实现

shader 反射 水面_2D水面波光效果 以及一些2D常用shader的实现

时间:2022-11-01 10:51:13

相关推荐

shader 反射 水面_2D水面波光效果 以及一些2D常用shader的实现

水面模拟是游戏行业的一个常见问题。在3D领域,水面模拟技术已经非常成熟,无论是unity也好,ue4也好,都有很多现成的代码可以直接拿来使用。不过在2D游戏中,水面模拟的开源实现还不是太多,跟3D完全不是一个数量级。接下来,本文就为大家贡献一个基于shader的水面效果的实现。

先来看下实际的显示效果(不带水底物和水中动物,原因后文会说),用了两种深浅不同的背景色:知乎视频​知乎视频​

视频中的效果只包含水面的波光,水底物(例如沉船礁石废墟什么的)和水中动物的资源涉及到知识产权问题不能放出来。在实际应用中,按照蓝色背景图-水底物-水中动物-水面波光的顺序从下到上依次排列,对水底物和水中动物添加一个或几个正弦波扭动叠加即可实现完整的水面效果,正弦波扭动的shader我也会放在后文中。

接下来,直接进入实现环节。

首先,你需要一张只包含黑白两色的噪声图,可以在3D软件中生成,也可以在shader中实现,3D软件生成的看起来会更自然可控一些,在这里我就用软件生成的了:

接下来,对这张噪声图依次做下面的处理:

1、与背景的混合模式(BlendMode)设置为GL_ONE, GL_ONE,混合模式的作用是决定这张图与其下面的图是如何叠加显示到屏幕上的。设置之后,运行效果是下图这个样子的:

2、下面进入shader环节,本文实现时使用的是cocos2dx引擎,对上面的噪声图应用下面的fragment shader:

varying vec2 v_texCoord;

varying vec4 v_position;

uniform float sinA;

uniform float cosA;

uniform float time;

uniform float speed;

void main() {

vec2 v_texCoord1=loopMove(sinA,cosA,time,speed,v_texCoord);

vec4 color1=texture2D(CC_Texture0, v_texCoord1);

vec2 v_texCoord2=loopMove(-sinA,-cosA,time,speed*0.8,v_texCoord);

vec4 color2=texture2D(CC_Texture0, v_texCoord2);

gl_FragColor = color1*color2;

gl_FragColor *= spotLight(v_position);

}

第一二行,采样时让噪声图朝指定方向以外部参数speed的速度进行循环移动,采点。

第三四行,让噪声图朝反方向以0.8*speed的速度进行循环移动,采点。

第五行,两个采样点的rgba值进行最简单的乘法叠加。

第六行,应用点光源,让最终的图像只有光源照到的地方会亮。

其中四个外部传进来的变量:sinA,cosA,time,speed。角度A是让噪声图进行移动时的方向,由于这个角度是不会变的,所以直接在程序开始时把角度值的正弦余弦都算好传进来即可。time是程序运行时间,不断增加,而speed是进行循环移动的速度。循环移动的效果和代码如下所示:知乎视频​

vec2 loopMove(float sinA,float cosA,float time,float speed,vec2 texCoord){

float offsetX = v_texCoord.x-time*speed*cosA;

float offsetY = v_texCoord.y-time*speed*sinA;

offsetX = 1.0 - (ceil(offsetX) - offsetX);

offsetY = 1.0 - (ceil(offsetY) - offsetY);

return vec2(offsetX,offsetY);

}

经过两个循环移动的叠加之后,波光闪动的效果已经出来了,只是现在全屏都是一样亮的,为了显得更真实,我们再添加一个点光源,让波光从屏幕左上角向周围逐渐变暗:

vec4 spotLight(vec4 position){

vec3 lightPos = vec3(200.0,988.0,50.0);//光源位置

vec4 result_color = vec4(1.0,1.0,1.0,1.0);

vec3 lightdir = lightPos - position.xyz;

vec3 att_dist;

att_dist.x = 1.0;

att_dist.z = dot(lightdir,lightdir);

att_dist.y = sqrt(att_dist.z);

float att_factor = 1.0/dot(att_dist,vec3(0.5,0.0001,0.00002));

lightdir = normalize(lightdir);

result_color *= att_factor;

return result_color;

}

该点光源是对标准点光源的简化,去掉了在本demo中不需要的计算和参数。完整的标准点光源实现代码是下面这个样子的,有兴趣的同学可以实现一下,调整下参数,还是很有意思的:

uniform vec3 lightPos; // 光源位置, vec3(200.0,988.0,100.0)

uniform vec4 lightAmbientColor; // 环境光的颜色, vec4(1.0,1.0,1.0,1.0)"

uniform vec4 lightDiffuseColor; // 散射光的颜色, vec4(1.0,1.0,1.0,1.0)

uniform vec4 lightSpecularColor; // 反射光的颜色, vec4(1.0,1.0,1.0,1.0)

uniform vec4 materialAmbientColor; // 材料的环境光颜色, vec4(1.0,1.0,1.0,1.0)

uniform vec4 materialDiffuseColor; // 材料的散射光的颜色, vec4(1.0,1.0,1.0,1.0)

uniform float materialSpecularExponent; // 材料的反射系数, 1.0

uniform vec4 materialSpecularColor; // 材料的反射光颜色, vec4(1.0,1.0,1.0,1.0)

uniform float lightSpotExponent; // 光的方向衰减系数, 设置lightDirection方向之后才好使, 5.0

uniform vec3 lightDirection; // 光线的方向, 设置lightAngleRange范围之后才好使, vec3(1.0,-2.0,-0.6)

uniform float lightAngleRange; // 光的扩散范围,设置为0的时候向四面八方发射,不为0的时候表示只向该角度内发射 0.0

uniform vec3 eyePos; // 视线的方向, vec3(0.0,-1.0,1.732)

vec4 spotLight(vec3 normal,vec4 position){

vec4 result_color = vec4(0.0,0.0,0.0,0.0);

vec3 lightdir = lightPos - position.xyz;

vec3 att_dist;

att_dist.x = 1.0;

att_dist.z = dot(lightdir,lightdir);

att_dist.y = sqrt(att_dist.z);

float att_factor = 1.0/dot(att_dist,vec3(0.5,0.0002,0.00008));

lightdir = normalize(lightdir);

if(lightAngleRange!=0.0){

float spot_factor = dot(-lightdir, normalize(lightDirection));

if(spot_factor >= cos(radians(lightAngleRange)))

spot_factor = pow(spot_factor, lightSpotExponent);

else

spot_factor = 0.0;

att_factor *= spot_factor;

}

if(att_factor>0.0){

result_color += (lightAmbientColor * materialAmbientColor);

float ndotl = max(0.0,dot(normal,lightdir));

result_color += (ndotl * lightDiffuseColor * materialDiffuseColor);

vec3 halfplane = normalize(lightdir + normalize(eyePos));

float ndoth = dot(normal, halfplane);

if (ndoth > 0.0)

result_color += (pow(ndoth, materialSpecularExponent) * materialSpecularColor * lightSpecularColor);

result_color *= att_factor;

}

return result_color;

}

点光源一叠加,就实现了本文开头视频中的效果。在实际项目应用中,把水底物和水中动物放到噪声图的下层,添加正弦波扭动效果,就可以实现非常逼真的海水效果。

正弦波叠加扭动的效果和fragment shader在下面,在这里我用了四个正弦波做叠加,至于里面的具体数值只是我反复试出来觉得效果还不错的,实际应用中可以根据自身需求的效果做调整:知乎视频​

varying vec4 v_fragmentColor;

varying vec2 v_texCoord;

varying vec4 v_position;

varying vec4 g_position;

uniform float time;

uniform float wavelength;//波长

uniform float amplitude;//振幅

float randFunc(float x,float y){//随机数生成器

return fract(sin(x*12.9876+y*4.1427)*43758.5453);

}

void main() {

float rand1=randFunc(v_position.x,v_position.y);

float rand2=randFunc(v_position.x*rand1,v_position.y*rand1);

float rand3=randFunc(v_position.x*rand2,v_position.y*rand2);

float rand4=randFunc(v_position.x*rand3,v_position.y*rand3);

float param1=amplitude*sin(sqrt((v_texCoord.x+0.09)*(v_texCoord.x+0.09)+(v_texCoord.y+0.09)*(v_texCoord.y+0.09))*wavelength*1.31 - time*1.23);

float param2=amplitude*sin(sqrt((v_texCoord.x-1.11)*(v_texCoord.x-1.11)+(v_texCoord.y+0.11)*(v_texCoord.y+0.11))*wavelength*1.13 - time*1.09);

float param3=amplitude*sin(sqrt((v_texCoord.x+0.13)*(v_texCoord.x+0.13)+(v_texCoord.y-1.13)*(v_texCoord.y-1.13))*wavelength*0.97 - time*0.91);

float param4=amplitude*sin(sqrt((v_texCoord.x-1.07)*(v_texCoord.x-1.07)+(v_texCoord.y-1.08)*(v_texCoord.y-1.08))*wavelength*0.83 - time*0.79);

vec2 v_texCoord1=v_texCoord + rand1*param1 + rand2*param2 + rand3*param3 + rand4*param4;

gl_FragColor = texture2D(CC_Texture0, v_texCoord1)*v_fragmentColor;

}

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。