我正在尝试在 HLSL 中为 XNA 项目创建一个动画太阳。
我使用 Perlin Noise 算法在像素着色器中生成纹理,这是我从this site学到的。
这是我为像素着色器编写的hlsl代码:
sampler myTexture;
struct VS_Output{
float4 Position : POSITION0;
float4 Color : COLOR0;
float2 TextCoord : TEXCOORD0;
};
float CosineInterpolation( float x, float y, float fractional ) {
float ft = 3.141592f * fractional;
float f = ( 1.0f - cos( ft ) ) * 0.5f;
return x * ( 1.0f - f ) + y * f;
}
float Noise(float2 xy)
{
float2 noise = (frac(sin(dot(xy ,float2(12.9898,78.233)*2.0)) * 43758.5453));
return abs(noise.x + noise.y) * 0.5;
}
float SmoothNoise( float integer_x, float integer_y ) {
float corners = ( Noise( float2(integer_x - 1, integer_y - 1) ) + Noise( float2(integer_x + 1, integer_y + 1 )) + Noise( float2(integer_x + 1, integer_y - 1 )) + Noise( float2(integer_x - 1, integer_y + 1 )) ) / 16.0f;
float sides = ( Noise( float2(integer_x, integer_y - 1 )) + Noise( float2(integer_x, integer_y + 1 )) + Noise( float2(integer_x + 1, integer_y )) + Noise( float2(integer_x - 1, integer_y )) ) / 8.0f;
float center = Noise( float2(integer_x, integer_y )) / 4.0f;
return corners + sides + center;
}
float InterpolatedNoise( float x, float y ) {
float integer_x = x - frac(x), fractional_x = frac(x);
float integer_y = y - frac(y), fractional_y = frac(y);
float p1 = SmoothNoise( integer_x, integer_y );
float p2 = SmoothNoise( integer_x + 1, integer_y );
float p3 = SmoothNoise( integer_x, integer_y + 1 );
float p4 = SmoothNoise( integer_x + 1, integer_y + 1 );
p1 = CosineInterpolation( p1, p2, fractional_x );
p2 = CosineInterpolation( p3, p4, fractional_x );
return CosineInterpolation( p1, p2, fractional_y );
}
float CreatePerlinNoise( float x, float y ) {
float result = 0.0f, amplitude = 0.0f, frequency = 0.0f, persistance = 0.1f;
for ( int i = 1; i <= 4; i++ ) {
frequency += 2;
amplitude += persistance;
result += InterpolatedNoise( x * frequency, y * frequency ) * amplitude;
}
return result;
}
float4 ps_main(VS_Output Input) : COLOR0
{
float index = CreatePerlinNoise(Input.TextCoord.x*256.0f, Input.TextCoord.y*256.0f);
return tex2D(myTexture, index);
}
基本上,在此代码中,通过将纹理坐标组件(TextCoord
)传递给CreatePelinNoise
函数,它返回一个值,该值用作渐变纹理的颜色索引({{1} } 1px x 256px ):
AMD RenderMonkey 的结果如下:
但是在球体的两极处存在难看的不良效果,这会使生成的纹理不均匀:
如何解决此问题并使生成的纹理均匀?
答案 0 :(得分:4)
这是因为您将噪声函数视为2D纹理,如果投影到球体上,将拉伸/扭曲。您基本上是在像素着色器中实时生成2D纹理,然后将其应用于球体。
如果您真的了解Perlin Noise原则,那么您可以将代码推广到两个以上的维度。肯·佩林本人虽然在发明噪声算法时也有同样的想法; in his own words,“将物体浸入程序性纹理(噪音)材料的汤中。”
只需使用像素坐标作为3D噪声函数(float4位置变量)的输入,保持颜色映射代码不变。这应该会产生预期的效果。 顺便说一下,如果你想要它的动画,将它推广到4D并随时间改变第四个参数。如果做得好,它应该会产生一个很好的“熔岩行星”效果(查看Perlin的幻灯片以获取更多信息)。
哦,加速提示:使用着名的3 * t ^ 2-2 * t ^ 3插值曲线,而不是使用cossine插值。在0和1之间,它看起来几乎与谐波相同,但成本计算方式较少。