Ich habe eine Szene mit 150000 Instanzen. Ich benutze glsl und opengl 4.0. Shader A ist 2-mal langsamer als Shader BIe, wobei Shader AI 20 fps und Shader BI durchschnittlich 40 fps erreicht. Was kann ich tun, um Shader A zu verbessern?
Shader A:
#version 400
struct Light {
vec3 position;
vec3 intensities; //a.k.a the color of the light
float ambientCoefficient;
float attenuation;
};
uniform bool useLight;
uniform mat4 modelMatrix;
uniform bool useTex;
uniform sampler2D tex;
uniform Light light;
uniform vec4 diffuseColor;
in vec2 fragTexCoord;
in vec3 fragNormal;
in vec3 fragVert;
out vec4 finalColor;
void main() {
vec3 normal = normalize(transpose(inverse(mat3(modelMatrix))) * fragNormal);
vec3 surfacePos = vec3(modelMatrix * vec4(fragVert, 1));
vec4 surfaceColor = vec4(0,0,0,1);
if(useTex) {
surfaceColor = texture(tex, fragTexCoord);
}
else {
//surfaceColor = diffuseColor;
surfaceColor = vec4(0,1,0,1);
}
if(useLight) {
vec3 surfaceToLight = normalize(light.position - surfacePos);
//ambient
vec3 ambient = light.ambientCoefficient * surfaceColor.rgb * light.intensities;
//diffuse
float diffuseCoefficient = max(0.0, dot(normal, surfaceToLight));
vec3 diffuse = diffuseCoefficient * surfaceColor.rgb * light.intensities;
//attenuation
float distanceToLight = length(light.position - surfacePos);
float attenuation = 1.0 / (1.0 + light.attenuation * pow(distanceToLight, 2));
//linear color (color before gamma correction)
vec3 linearColor = ambient + attenuation*(diffuse);
//final color (after gamma correction)
vec3 gamma = vec3(1.0/2.2);
finalColor = vec4(pow(linearColor, gamma), surfaceColor.a);
}
else {
finalColor = surfaceColor;
}
}
Shader B:
#version 400
struct Light {
vec3 position;
vec3 intensities; //a.k.a the color of the light
float ambientCoefficient;
float attenuation;
};
uniform bool useLight;
uniform mat4 modelMatrix;
uniform bool useTex;
uniform sampler2D tex;
uniform Light light;
uniform vec4 diffuseColor;
in vec2 fragTexCoord;
in vec3 fragNormal;
in vec3 fragVert;
out vec4 finalColor;
void main() {
finalColor = vec4(0,0,0.7,1);
}
Antworten:
Zunächst sollten Sie so viele Daten wie möglich vorberechnen und vermeiden, für jedes Pixel dieselben Werte zu berechnen.
Du hast so ein Fragment:
Dies invertiert die Matrix, was keine so triviale Operation ist, und trotz der Tatsache, dass die Eingabedaten für jedes Pixel gleich sind (die Ergebnisse sind also gleich), wird dies für jedes Pixel neu berechnet. Berechnen Sie es einmal vor dem Rendern und übergeben Sie das Ergebnis als eine andere Matrix, wie Sie es mit dem tun
modelMatrix
.Später normalisieren Sie auch den
(light.position - surfacePos)
Vektor, berechnenlength
ihn aber auch, sodass zweisqrt
Operationen anstelle von einer ausgeführt werden.Abhängig von Ihrer Hardware kann es außerdem vorkommen, dass die Verwendung
if's
in einem Pixel-Shader Ihre Leistung beeinträchtigt. In diesem Fall können Sie einige unterschiedliche Versionen Ihres Shaders vorbereiten und Ihre Instanzen je nachuseLight
unduseTex
Eigenschaften stapeln.BEARBEITEN:
Sie können auch versuchen, die in Shadern definierte OpenGL-Version zu verringern, um die niedrigste Version zu sein, die Ihre Funktionen unterstützt. Theoretisch sollte es nicht viel bewirken, aber je nach Treiber und HW-Anbieter kann die Praxis unterschiedlich sein ... (Wenn Ihre GPU OGL 4.0 unterstützt, bedeutet dies häufig, dass es in OGL 3.0 schnell, in 4.0 jedoch sehr langsam ist, aber Sie müssen testen es auf bestimmten Fall).
quelle
inverse
konstant ist, gibt es auf der GPU keinen Platz zum Speichern dieser Daten. Es funktioniert einfach nicht so (AFAIK).transpose(inverse(mat3(modelMatrix))
Sollte die Transponierung einer 3x3-Rotationsmatrix nicht schon umgekehrt sein?Betrachtet man insbesondere diese Zeile:
vec3 normal = normalize(transpose(inverse(mat3(modelMatrix))) * fragNormal);
Das Ausarbeiten der Umkehrung einer Matrix ist sehr anstrengend und sollte von der CPU vorberechnet werden, anstatt die GPU zu zwingen, Zeit damit zu verschwenden, damit herumzuspielen.
quelle
Sie können nicht viel tun. Die einfache Realität ist, dass Shader A mehr Arbeit leistet als Shader B, sodass er immer langsamer läuft.
Wir können die Lücke jedoch etwas schließen. Ich kann Ihnen keine eindeutigen Zahlen dafür geben, wie viel davon abhängt. Dies hängt von den Leistungsmerkmalen des restlichen Programms ab. Behandeln Sie diese also als allgemeine bewährte Verfahren.
Das sind viele Inversionen und Transpositionen pro Frame. Tun Sie dies stattdessen nur einmal auf der CPU (oder zumindest nur, wenn sich modelMatrix ändert) und senden Sie die inverse / transponierte Matrix als zusätzliche Uniform. Wenn ALU-Operationen ein Engpass für Sie sind, sollte dies Ihnen den größten Anstieg bringen.
Verzweigen ist nicht der Tod, der es früher war, aber Sie können es hier trotzdem vermeiden (und einen einheitlichen Schlitz speichern), indem Sie eine 1x1-Textur (der entsprechenden Farbe) erstellen und diese stattdessen binden.
Mehr Verzweigung. In diesem Fall gibt es keine offensichtliche Alternative (wie die 1x1-Textur), daher würde ich Sie ermutigen, diese Bedingung in einen dritten Shader aufzuteilen und beide zu vergleichen (dh 2 Shader mit einem Zweig gegenüber 3 Shadern ohne). Je nachdem, wie oft Sie die Shader wechseln müssen, kann es im Vergleich zur Verzweigung zu Leistungsunterschieden kommen oder auch nicht.
quelle
if
, kann es sich nur auf einen Zweig vorbereiten. Wenn Sie den zweiten auswählen, wird diese Arbeit verschwendet und muss neu gestartet werden. Das gleiche passiert auf CPUs. Der Trick mit 1x1-Textur kann helfen oder nicht - alles hängt von einem bestimmten Shader, GPU-Hersteller, Architektur, Treibern usw. ab, sodass Sie ihn nur selbst testen müssen.