作者: Admin
\N实现卡通渲染关键是要写自己的光照函数, 通过将光照程序计算出来的片段颜色,来查找一个1D的纹理,来将整个色域分解成少数几种离散的颜色, 最终将颜色输出, 呈现卡通渲染.
在这个程序中, 我没有使用1D纹理, 原因有几个, 首先1D数据不好获得, 不知道用什么编辑器去编辑一个阶梯的变化序列, 其次就是1D纹理不直观,无法直接观察效果, 所以我选用了2D纹理, 这下好了, 可以用PS等工具去直接创造个阶梯函数, 另外使用2D纹理还有一个好处, 这个在后文讲解.
当然使用了2D纹理, 就不能再用1D纹理的查找函数了, 需要用2D纹理的查找函数. 其实这个变换很简单.
原来查询1D纹理,是这样的:
\NdiffuseLighting = tex1D( diffuseRamp, diffuseLighting).x;
\NdiffuseRamp是个1D的纹理, diffuseLighting就是通过光照程序计算出来的diffuse值, 是一个float值.
因为diffuseLight是个float值, 最后我们就取x分量了.
下面我们把他改成查询2D纹理的.
\NdiffuseLighting = tex2D( diffuseRamp, float2(diffuseLight, 0.5)).x;
\N这样就用2D查询代替了1D查询, 虽然在数据上废点硬盘空间,但这是值得的.
下面来简单说下如何用PS建立个拥有阶梯函数的2D纹理.
看下图,我们建立个256X16的纹理, 理论上256X1,16X1,4X1都可以, 如果你的眼睛能看的清楚也无所谓, 我眼睛差,太小了,看不清楚,所以我选择256X16的大小.
\N\N
左边黑右边白,这就是个阶梯函数, 你可以调整分界点的位置, 来控制颜色过滤的严格程度, 自己调调看看效果就明白了.
使用这个纹理来查询来控制颜色有个缺点,漫反射,镜面反射和边界测试太严格了:当物体移动的时候, 他们的结果趋向于相当明显的起伏. 解决这个问题, 我们可以用一个平滑的过度了来代替, 如下图:
\N\N
我将卡通渲染的vertex program 和 fragment program 贴出来
\N//vertex program
// This binding semantic requires CG_PROFILE_ARBVP1 or higher.
uniform float4x4 modelViewProj : state.matrix.mvp;
void main( float4 position : POSITION,
float3 normal : NORMAL,
out float4 oPosition : POSITION,
out float diffuseLight: TEXCOORD0,
out float specularLight: TEXCOORD1,
out float edge : TEXCOORD2,
uniform float3 lightPosition,
uniform float3 eyePosition,
uniform float shininess)
{
oPosition = mul(modelViewProj, position);
// Calculate diffuse lighting
float3 N = normalize(normal);
float3 L = normalize(lightPosition - position.xyz);
diffuseLight = max(dot(N, L), 0);
// Calculate specular lighting
float3 V = normalize(eyePosition - position.xyz);
float3 H = normalize(L + V);
specularLight = pow(max(dot(N, H), 0), shininess);
if (diffuseLight <= 0) specularLight =0;
//Perform edge detection
edge = max(dot(N, V), 0);
}
//fragment program
void main(float diffuseLight : TEXCOORD0,
float specularLight : TEXCOORD1,
float edge : TEXCOORD2,
out float4 color : COLOR,
uniform float4 Kd,
uniform float4 Ks,
uniform sampler2D diffuseRamp,
uniform sampler2D specularRamp,
uniform sampler2D edgeRamp)
{
//Apply step functions
diffuseLight = tex2D(diffuseRamp, float2(diffuseLight, 0.5)).x;
specularLight = tex2D(specularRamp, float2(specularLight, 0.5)).x;
edge = tex2D(edgeRamp, float2(edge, 0.5)).x;
// Compute the final color
color = edge * (Kd * diffuseLight + Ks * specularLight);
}
\N
演示的源代码和执行程序下载:
点击下载
CG库下载,如果没有请下载拷贝的SYSTEM32目录下,否则无法运行
点击下载
演示截图:
\N\N
\N\N
www.azure.com.cn