shader ConeVolumeShadow: Unterschied zwischen den Versionen
(Die Seite wurde neu angelegt: „{{Offline}} =Shadername= Zurück zur Shadersammlung {|{{Prettytable_B1}} width=100% !width=60%|Beschreibung !width=20%|Autor !width=20%|Version |- |Kegelvolu…“) |
|||
Zeile 20: | Zeile 20: | ||
== Voraussetzungen == | == Voraussetzungen == | ||
− | + | Aufgrund der besonderen Funktionsweise funktioniert dieser Shader nur dann wenn es sich sämtlichen Objekte in der Szene die einen Schatten werfen sollen um Kugeln handelt. Des weiteren muss innerhalb der Anwendung manuell berechnet werden welche Objekte auf welches Objekt einen Schatten werfen. Je mehr Objekte ein Schatten auf ein einzelnes Objekt werfen, desto langsamer wird das Verfahren. | |
== Funktionsweise == | == Funktionsweise == | ||
+ | Verwendet man eine gewöhnliche Punktlichtquelle hat das Schatten-Volumen einer Kugel eine sehr einfache Form, nämliche die eines einfachen Kreiskegels. Selbst wenn wir statt der Punktlichtquelle eine Kugellichtquelle verwenden lässt sich das Schattenvolumen noch recht leicht durch zwei Kegel beschreiben. Dies machen wir uns zu nutze. | ||
+ | |||
[[Bild:shader_ConeVolumeSoftShadow.png]] | [[Bild:shader_ConeVolumeSoftShadow.png]] | ||
Zeile 49: | Zeile 51: | ||
====Fragmentshader==== | ====Fragmentshader==== | ||
+ | Der Fragmentshader macht den eigentlich Job. Es handelt sich um einen aufgeblasenen Per-Pixel-Light-Shader. Zusätzlich zur normalen Diffuse-Beleuchtung wird in der Funktion <tt>shadowFactor()</tt> auch noch ein Schattenfaktor berechnet. Diese Funktion berechnet das Produkt sämtlicher Kegelvolumen für das aktuelle Fragment. Am Ende erhalten wir einen Wert zwischen 0.0 und 1.0 der dann einfach mit der Diffuse-Beleuchtung verrechnet wird. | ||
<source lang="glsl">uniform sampler2D uTexture; | <source lang="glsl">uniform sampler2D uTexture; | ||
Version vom 30. Januar 2010, 21:27 Uhr
Bitte haben Sie etwas Geduld und nehmen Sie keine Änderungen vor, bis der Artikel hochgeladen wurde. |
Inhaltsverzeichnis
Shadername
Zurück zur Shadersammlung
Beschreibung | Autor | Version |
---|---|---|
Kegelvolumen-Schatten | Coolcat | 1.0 |
Bilder
Voraussetzungen
Aufgrund der besonderen Funktionsweise funktioniert dieser Shader nur dann wenn es sich sämtlichen Objekte in der Szene die einen Schatten werfen sollen um Kugeln handelt. Des weiteren muss innerhalb der Anwendung manuell berechnet werden welche Objekte auf welches Objekt einen Schatten werfen. Je mehr Objekte ein Schatten auf ein einzelnes Objekt werfen, desto langsamer wird das Verfahren.
Funktionsweise
Verwendet man eine gewöhnliche Punktlichtquelle hat das Schatten-Volumen einer Kugel eine sehr einfache Form, nämliche die eines einfachen Kreiskegels. Selbst wenn wir statt der Punktlichtquelle eine Kugellichtquelle verwenden lässt sich das Schattenvolumen noch recht leicht durch zwei Kegel beschreiben. Dies machen wir uns zu nutze.
Code
Vertexshader
Der Vertexshader ist nichts besonderes. Er transformiert einfach nur die Vertexposition sowie die Normale und gibt die Daten weiter. Dieser Shader ist im wesentlichen aus UltimateConquest übernommen. Daher werden hier Vertexattribute und Matrixuniforms selbst definiert. Sofern man noch mit OpenGL 2.x arbeitet kann man aber problemlos die dort verfügbaren eingebauten Uniforms und Attribute benutzen.
uniform mat4 uModelViewProjection;
uniform mat4 uModelView;
uniform mat3 uNormalMatrix;
attribute vec3 aPosition;
attribute vec3 aNormal;
attribute vec2 aTexCoord;
varying vec3 vPosition;
varying vec3 vNormal;
varying vec2 vTexCoord;
void main() {
gl_Position = uModelViewProjection * vec4(aPosition, 1.0);
vPosition = (uModelView * vec4(aPosition, 1.0)).xyz;
vNormal = normalize(uNormalMatrix * aNormal);
vTexCoord = aTexCoord;
}
Fragmentshader
Der Fragmentshader macht den eigentlich Job. Es handelt sich um einen aufgeblasenen Per-Pixel-Light-Shader. Zusätzlich zur normalen Diffuse-Beleuchtung wird in der Funktion shadowFactor() auch noch ein Schattenfaktor berechnet. Diese Funktion berechnet das Produkt sämtlicher Kegelvolumen für das aktuelle Fragment. Am Ende erhalten wir einen Wert zwischen 0.0 und 1.0 der dann einfach mit der Diffuse-Beleuchtung verrechnet wird.
uniform sampler2D uTexture;
// the lightsource described by position and radius
uniform vec4 uLightsource;
vec3 lsPosition = uLightsource.xyz;
float lsRadius = uLightsource.w;
// each shadow volume is described by position (xyz) and radius (w) of the occluder packed into a single vec4
const int shadowMaxCount = 5;
uniform vec4 uShadows[shadowMaxCount];
uniform int uShadowCount;
varying vec3 vPosition;
varying vec3 vNormal;
varying vec2 vTexCoord;
const vec3 cAmbient = vec3(0.025,0.025,0.025);
// simple local diffuse per-pixel lighting
float diffuseFactor() {
vec3 lightDir = normalize(lsPosition-vPosition);
vec3 normal = normalize(vNormal);
return max(dot(lightDir, normal), 0.0);
}
// process all shadow volumes
float shadowFactor() {
float s = 1.0;
// iterate all shadow volumes
for (int i=0; i<uShadowCount; ++i) {
// extract data
vec4 occluder = uShadows[i];
vec3 ocPosition = occluder.xyz;
float ocRadius = occluder.w;
// project fragment (vPosition) on the cone axis => F_
vec3 nvLO = ocPosition - lsPosition;
float dLO = length(nvLO);
nvLO /= dLO;
vec3 vLF = vPosition - lsPosition;
float dLF_ = dot(vLF, nvLO);
if (dLF_ < dLO) {
// fragment before occluder => no shadow
continue;
}
vec3 F_ = lsPosition + dLF_ * nvLO;
float rF = distance(F_, vPosition);
// compute outer and inner radius at F_
float rF_outer = (ocRadius + lsRadius) * (dLF_ / dLO) - lsRadius;
if (rF >= rF_outer) {
// outside the outer cone => no shadow
continue;
}
float rF_inner = (ocRadius - lsRadius) * (dLF_ / dLO) + lsRadius;
if (rF_inner >= rF) {
// inside the inner cone => full shadow
return 0.0;
}
else if (rF_inner >= 0.0 || rF >= -rF_inner) {
// soft shadow, linear interpolation
s *= (rF - rF_inner) / (rF_outer - rF_inner);
}
else {
// light from both sides of the occluder
s *= (-2.0*rF_inner) / (rF_outer - rF_inner);
}
}
return s;
}
void main() {
vec3 texcolor = texture2D(uTexture, vTexCoord).xyz;
gl_FragColor.xyz = cAmbient * texcolor;
float lightFactor = diffuseFactor();
if (lightFactor > 0.004) {
// don't compute shadows for fragments that are already dark from local lighting
lightFactor *= shadowFactor();
}
gl_FragColor.xyz += texcolor * lightFactor;
gl_FragColor.w = 1.0;
}