视差贴图 parallax map


视差贴图,不同于法线贴图的是,会根据视角调整纹理映射的偏移,从而产生遮挡效果

但在多边形的边缘仍然是平的。

(这个需要用浮雕映射来解决,利用gs阶段,输出更多的顶点,从而产生新的多边形,
需要硬件能支持相应的渲染管线,题外话,此处略过不表,
笔者认为如果边缘一般距离视点较远的话,一般情况下影响的效果有限)

首先要有一个高度图

根据视角,以观察到的点为终点,以纹理的最大高度(1*height_scale),根据视线向量,倒退到起点
可以设定一个step(可配置,step越小,采样次数越多,准确率越高,但帧率也会下降)
每隔一个step,采样出当前纹理的高度值tex_h,与当前视点的高度view_h比较,
如果view_h > tex_h 查找继续.否则停止.

为了更精细查找,nvidia还会在找到的step区间更进一步细分,但过程同上述一样,不表.


光照:

光线变换至切空间之后,法线与光线做个点积即可得到光照强度与颜色


阴影:

根据修正后的采样位置,顺着光线的方向,取高度纹理采样,与视点高度比较,计算对阴影的贡献。


glsl顶点着色器代码:

attribute vec3 attr_ant_tangent;
attribute vec3 attr_ant_binormal;
attribute vec3 attr_ant_normal;

uniform vec4 uni_light_pos;/* 此处假设光源已转换至局部坐标系 */
uniform vec3 uni_eye_local_pos;/* 视点位置也需要变换至局部坐标系 */

varying vec3 tangent_light_dir;
varying vec3 tangent_eye_dir;
varying vec3 tangent_light_dir_shadow;

varying vec3 local_pos;/* 局部坐标系 */

void main()
{
	vec3 light_dir = normalize(uni_light_pos.xyz - gl_Vertex.xyz);/* 如果是平行光,则光线方向不变,w=1说明是点光源,根据光源与顶点,得到光线向量 */
	mat3 rotation            = mat3(attr_ant_binormal, attr_ant_tangent, attr_ant_normal);
	tangent_light_dir        = normalize(rotation * light_dir);

	mat3 rotation_shadow     = mat3(attr_ant_tangent, attr_ant_binormal, attr_ant_normal);
	tangent_light_dir_shadow = normalize(rotation_shadow * light_dir);
	
	vec3 eye_dir   = uni_eye_local_pos - gl_Vertex.xyz;
	mat3 rotation_eye = mat3(attr_ant_tangent, attr_ant_binormal, attr_ant_normal);
	tangent_eye_dir = normalize(rotation_eye * eye_dir);

	local_pos = gl_Vertex.xyz;
	gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;	
	
	gl_TexCoord[0] = gl_MultiTexCoord0;
}


 片元着色器代码: 
uniform sampler2D uni_tex_color1;
uniform sampler2D uni_tex_color2;
uniform sampler2D uni_tex_normal;
uniform vec3 uni_eye_local_pos;/* 视点位置也需要变换至局部坐标系 */
uniform vec4 uni_light_pos;/* 此处假设光源已转换至局部坐标系 */

varying vec3 local_pos;/* 局部坐标系 */

varying vec3 tangent_light_dir;
varying vec3 tangent_eye_dir;
varying vec3 tangent_light_dir_shadow;

float step_count = 4.0;
float step_count_detail = 16.0;
float height_scale = 10.0 / 255.0;

void main()
{
	/* 根据view_pos */
	vec3 eye_dir = tangent_eye_dir / tangent_eye_dir.z;
	
	vec2 tex_sample_step = eye_dir.xy * (height_scale / step_count);
	vec2 tex_sample_start = gl_TexCoord[0].st + eye_dir.xy * height_scale;
	
	float eye_height = height_scale;
	float height_step = eye_height / step_count;

	vec4 tex_sample;
	vec4 tex_color;
	
	vec2 tex_sample_last = tex_sample_start;
	float eye_height_last = eye_height;
	for(int i = 0; i < step_count; i ++)
	{
		tex_sample = texture2D(uni_tex_normal, tex_sample_start);

		if(tex_sample.a * height_scale >= eye_height)
			break;

		tex_sample_last = tex_sample_start;
		eye_height_last = eye_height;

		eye_height -= height_step;
		tex_sample_start -= tex_sample_step;
	}
	
	{
		height_step = height_step / step_count_detail;
		tex_sample_step = tex_sample_step / step_count_detail;
		
		tex_sample_start = tex_sample_last;
		eye_height = eye_height_last;
		for(int j = 0; j < step_count_detail; j ++)
		{
			tex_sample = texture2D(uni_tex_normal, tex_sample_start);

			if(tex_sample.a * height_scale >= eye_height)
				break;

			eye_height -= height_step;
			tex_sample_start -= tex_sample_step;
		}
	}
	
	/* cal shadow */
	float shadow = 0.0;
	{
		float light_sample_count = 32;
		vec3 light_dir = tangent_light_dir_shadow / tangent_light_dir_shadow.z;
		
		vec2 light_sample_start = gl_TexCoord[0].st + light_dir.xy * height_scale;		
		vec2 light_sample_step = light_dir.xy * (height_scale / light_sample_count);
	
		float light_height = height_scale;
		float light_step = light_height / light_sample_count;
		
		float real_h = tex_sample.a * height_scale;
		
		for(int k = 0; k < light_sample_count; k ++)
		{
			float tex_h = texture2D(uni_tex_normal, light_sample_start).a * height_scale;

			if(tex_h >= real_h)
			{
				shadow += min((tex_h - real_h)/(height_scale * 0.3), 1.0);
			}

			light_height -= light_step;
			light_sample_start -= light_sample_step;
		}

		shadow = (1- shadow / light_sample_count) * 0.8;
	}
	
	tex_color = texture2D(uni_tex_color1, tex_sample_start);
	
	vec3 normal = tex_sample.xyz;

	normal = (normal - 0.5) * 2.0;
	normal = normalize(normal);
	
	float dis = length(uni_light_pos.xyz - local_pos);
	float dis_factor = 1.0 / (1.0 + max(dis - 50, 0));

	vec3 local_light = normalize(tangent_light_dir);
	float normal_factor = clamp(dot(normal, local_light), 0.0, 1.0);

	gl_FragColor = dis_factor * normal_factor * tex_color * shadow;
	//gl_FragColor = shadow * tex_color;
	//gl_FragColor = dis_factor * normal_factor * shadow * vec4(1,1,1,1);

	gl_FragColor.a = 1.0f;
	
/*vec3 vtmp = normalize(tangent_eye_dir);
gl_FragColor.r = abs(vtmp.x);
gl_FragColor.g = abs(vtmp.y);
gl_FragColor.b = abs(vtmp.z);*/
}




笔者实现的效果


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值