HLSL像素着色器照明性能(XNA)
我有一个足够简单的着色器,支持多个点光源。HLSL像素着色器照明性能(XNA),xna,hlsl,pixel-shader,Xna,Hlsl,Pixel Shader,我有一个足够简单的着色器,支持多个点光源。 灯光存储为灯光结构数组(最大大小),当其更改时,我会传入活动灯光的数量。 问题出在PixelShader函数中: 这是基本的东西,从纹理中获得基本颜色,在灯光数组中循环0到NumacTivelills,并添加效果,效果很好,但性能很差 但是,如果我用一个相同值的常数替换对全局var numActiveLights的引用,性能就可以了。 我只是不明白为什么引用变量会产生30+fps的差异 谁能解释一下吗 完整着色器代码: #define MAX_POIN
灯光存储为灯光结构数组(最大大小),当其更改时,我会传入活动灯光的数量。
问题出在PixelShader函数中:
这是基本的东西,从纹理中获得基本颜色,在灯光数组中循环0到NumacTivelills,并添加效果,效果很好,但性能很差
但是,如果我用一个相同值的常数替换对全局var numActiveLights的引用,性能就可以了。
我只是不明白为什么引用变量会产生30+fps的差异 谁能解释一下吗 完整着色器代码:
#define MAX_POINT_LIGHTS 16
struct PointLight
{
float3 Position;
float4 Color;
float Radius;
};
float4x4 World;
float4x4 View;
float4x4 Projection;
float3 CameraPosition;
float4 SpecularColor;
float SpecularPower;
float SpecularIntensity;
float4 AmbientColor;
float AmbientIntensity;
float DiffuseIntensity;
int activeLights;
PointLight lights[MAX_POINT_LIGHTS];
bool IsLightingEnabled;
bool IsAmbientLightingEnabled;
bool IsDiffuseLightingEnabled;
bool IsSpecularLightingEnabled;
Texture Texture;
sampler TextureSampler = sampler_state
{
Texture = <Texture>;
Magfilter = POINT;
Minfilter = POINT;
Mipfilter = POINT;
AddressU = WRAP;
AddressV = WRAP;
};
struct VS_INPUT
{
float4 Position : POSITION0;
float2 TexCoord : TEXCOORD0;
float3 Normal : NORMAL0;
};
struct VS_OUTPUT
{
float3 WorldPosition : TEXCOORD0;
float4 Position : POSITION0;
float3 Normal : TEXCOORD1;
float2 TexCoord : TEXCOORD2;
float3 ViewDir : TEXCOORD3;
};
VS_OUTPUT VS_PointLighting(VS_INPUT input)
{
VS_OUTPUT output;
float4 worldPosition = mul(input.Position, World);
output.WorldPosition = worldPosition;
float4 viewPosition = mul(worldPosition, View);
output.Position = mul(viewPosition, Projection);
output.Normal = normalize(mul(input.Normal, World));
output.TexCoord = input.TexCoord;
output.ViewDir = normalize(CameraPosition - worldPosition);
return output;
}
float4 PS_PointLighting(VS_OUTPUT IN) : COLOR
{
if(!IsLightingEnabled) return tex2D(TextureSampler,IN.TexCoord);
float4 color = float4(0.0f, 0.0f, 0.0f, 0.0f);
float3 n = normalize(IN.Normal);
float3 v = normalize(IN.ViewDir);
float3 l = float3(0.0f, 0.0f, 0.0f);
float3 h = float3(0.0f, 0.0f, 0.0f);
float atten = 0.0f;
float nDotL = 0.0f;
float power = 0.0f;
if(IsAmbientLightingEnabled) color += (AmbientColor*AmbientIntensity);
if(IsDiffuseLightingEnabled || IsSpecularLightingEnabled)
{
//for (int i = 0; i < activeLights; ++i)//works but perfoemnce is terrible
for (int i = 0; i < 7; ++i)//performance is fine but obviously isn't dynamic
{
l = (lights[i].Position - IN.WorldPosition) / lights[i].Radius;
atten = saturate(1.0f - dot(l, l));
l = normalize(l);
nDotL = saturate(dot(n, l));
if(IsDiffuseLightingEnabled) color += (lights[i].Color * nDotL * atten);
if(IsSpecularLightingEnabled) color += (SpecularColor * SpecularPower * atten);
}
}
return color * tex2D(TextureSampler, IN.TexCoord);
}
technique PerPixelPointLighting
{
pass
{
VertexShader = compile vs_3_0 VS_PointLighting();
PixelShader = compile ps_3_0 PS_PointLighting();
}
}
#定义最大点灯16
结构点光源
{
浮动3位;
4色;
浮动半径;
};
四驱世界;
浮动4x4视图;
浮动4x4投影;
浮动3摄像机定位;
浮动4镜面颜色;
浮动镜面电源;
浮动镜面强度;
浮动4环境色;
漂浮环境强度;
漂浮扩散强度;
活动灯光;
点光源[最大点光源];
布尔孤岛亮起;
布尔是一个模糊的光源;
bool为DiffuseLightingEnabled;
布尔是特别明亮的;
纹理;
采样器纹理采样器=采样器状态
{
纹理=;
磁过滤器=点;
Minfilter=点;
Mipfilter=点;
地址u=包裹;
地址v=包裹;
};
结构与输入
{
浮动4位置:位置0;
浮动2 TexCoord:TEXCOORD0;
float3 Normal:NORMAL0;
};
结构与输出
{
浮动3世界位置:TEXCOORD0;
浮动4位置:位置0;
浮动3正常:TEXCOORD1;
浮动2 TexCoord:TEXCOORD2;
float3-ViewDir:TEXCOORD3;
};
VS_输出VS_点光源(VS_输入)
{
VS_输出;
float4 worldPosition=mul(input.Position,World);
output.WorldPosition=世界位置;
float4 viewPosition=mul(世界位置,视图);
输出位置=mul(视图位置、投影);
output.Normal=normalize(mul(input.Normal,World));
output.TexCoord=input.TexCoord;
output.ViewDir=规格化(CameraPosition-worldPosition);
返回输出;
}
float4 PS_点光源(VS_输出IN):颜色
{
如果(!IsLightingEnabled)返回tex2D(TextureSampler,IN.TexCoord);
float4 color=float4(0.0f,0.0f,0.0f,0.0f);
float3 n=正常化(IN.Normal);
float3 v=标准化(IN.ViewDir);
浮动3 l=浮动3(0.0f,0.0f,0.0f);
浮动3 h=浮动3(0.0f,0.0f,0.0f);
浮点数=0.0f;
浮动nDotL=0.0f;
浮动功率=0.0f;
如果(IsAmbientLightingEnabled)颜色+=(AmbientColor*AmbientIntensity);
如果(IsDiffuseLightingEnabled | | IsSpecialLightingEnabled)
{
//for(int i=0;i
我的猜测是,将循环约束更改为编译时常量将允许HLSL编译器展开循环。也就是说,与此相反:
for (int i = 0; i < 7; i++)
doLoopyStuff();
循环和条件分支在着色器代码中可能会对性能造成重大影响,应尽可能避免
编辑
这是我脑子里想不出来的,但也许你可以试试这样的东西
for (int i = 0; i < MAX_LIGHTS; i++)
{
color += step(i, activeLights) * lightingFunction();
}
for(int i=0;i
通过这种方式,您可以计算所有可能的灯光,但对于非活动灯光,始终会获得0的值。当然,好处将取决于照明功能的复杂性;您需要进行更多的评测。尝试使用PIX对其进行评测 或者,读一读这篇杂乱无章的推测: 也许是因为使用常量,编译器可以分解和折叠循环的指令。当您将其替换为变量时,编译器将无法进行相同的假设 不过,与您的实际问题有些无关,我会将许多条件/计算推到软件级别
if(IsDiffuseLightingEnabled | | IsSpecialLightingEnabled)
^-像那样
另外,我认为在调用着色器程序之前,可以预先计算一些东西。Likel=(lights[i].Position-IN.WorldPosition)/lights[i].Radius代码>传递一个预先计算的数组,而不是每次在每个像素上计算
我可能对HLSL编译器所做的优化有错误的了解,但我认为在像素着色器上进行的每一次计算都会在屏幕上执行w*h次(尽管这是非常并行的),我模糊地记得着色器中的指令数量有一些限制(比如72?)。(尽管我认为在HLSL的更高版本中,这一限制被放宽了很多)。可能是因为你的着色器生成了这么多指令——可能是它破坏了你的程序,并在编译时将其转换为一个多通道像素着色器。如果是这样的话,这可能会增加大量的开销
实际上,还有一个想法可能很愚蠢:将变量传递给着色器,使其将数据传输到GPU。这种传输是在带宽有限的情况下进行的。也许编译器足够聪明,当您只需要
for (int i = 0; i < MAX_LIGHTS; i++)
{
color += step(i, activeLights) * lightingFunction();
}