视差贴图 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);*/
}
笔者实现的效果