float4x4 view_proj_matrix;
float4 view_position;
struct VS_OUTPUT
{
float4 Pos: POSITION;
float2 TexCoord: TEXCOORD0;
float3 Refract: TEXCOORD1;
};
VS_OUTPUT vs_main(float4 inPos: POSITION, float3 inNormal: NORMAL,
float2 inTxr: TEXCOORD0)
{
VS_OUTPUT Out;
// Compute the projected position and send out the texture coordinates
Out.Pos = mul(view_proj_matrix, inPos);
Out.TexCoord = inTxr;
float3 viewVec = normalize(view_position - inPos);
// Compute the reflection vector using Snell’s Law
// the refract HLSL function does not always work properly
// n_i * sin(theta_i) = n_r * sin(theta_r)
// sin(theta_i) : Determine the sine of the incident vector
float cosine = dot(viewVec, inNormal);
float sine = sqrt(1 - cosine * cosine);
// sin(theta_r) : Determine cosine of the refracted vector
// Note that the saturate(x) function is equivalent to
// using clamp(0,1,x). Also, 1.14 is the IOR for this
// shader.
float sine2 = saturate(1.14 * sine);
float cosine2 = sqrt(1 - sine2 * sine2);
// Determine the refraction vector be using the normal and tangent
// vectors as basis to determine the refraction direction
float3 x = -inNormal;
float3 y = normalize(cross(cross(viewVec, inNormal), inNormal));
Out.Refract = x * cosine2 + y * sine2;
return Out;
}
Iš pixel'ių shader'ių pusės viskas ko reikia, tai su'sample'inti environment map ir išgauti spalvą kaip sekantis kodas daro:
sampler Wood;
sampler EnvMap;
float4 ps_main(float2 inTxr: TEXCOORD0,float3 inRefract: TEXCOORD1) : COLOR
{
// Output texture color with reflection map
return texCUBE(EnvMap,inRefract);
}
Šita funkcija:
cross(viewVec, inNormal)
duoda vektorinę vektorių sandaugą iš dviejų vektorių. Padarius vektorinę vektorių sandaugą tarp dviejų trimačių vektorių gaunamas vektorius sudarantis 90 laipsnių kampą tiek su viewVec, tiek su inNormal. Tarkim, g=cross(viewVec, inNormal), tada g vektorius sudaro 90 laipsnių kampą su viewVec vektoriumi ir 90 laipsnių kampą su inNormal vektoriumi. Vektorius g lygiagretus trikampio plokštumai.
Vektorius inNormal yra plokščio trikampio (sudaryto iš 3 vertex'ų) normalė (normalė visada sudaro 90 laipsnių kampą su plokštuma).
Kadangi, spėju, kad vektorius inNormal normalizuotas (visos vektoriaus koordinatės gali būti tik nuo iki ) ir viewVec vektorius normalizuotas, tai shader'yje šito cross(cross(viewVec, inNormal), inNormal) normalizuoti greičiausiai nereikėjo (nebūtina).
Vektoriui y priešingos krypties vektorius yra vektorius h:
h = -y = -cross(g, inNormal).
Vektoriaus h kiekviena koordinatė turi priešingą ženklą (+ arba -) negu vektoriaus y. Vektorius h yra lygaigretus trikampio plokštumai (vektorius h guli ant trikampio plokštumos kaip ir vektorius g). Vektorius h su vektoriumi g sudaro 90 laipsnių kampą ant trikampio plokštumos (vektorius y taip pat sudaro 90 laipsnių kampą su vektoriumi g).
VEKTORIUS h YRA PROJEKCIJA VEKTORIAUS viewVec ANT TRIKAMPIO PLOKŠTUMOS. Vektoriaus projekcija ant plokštumos yra trumpiausias atstumas nuo vektoriaus galo iki plokštumos.
Duotas trikampis, kurio taškai (viršūnės) yra A(3; 4; 5), B(6; 7; 8), C(9; 10; 11). Rasime vektorius AB ir AC.
AB={6-3; 7-4; 8-5}={3; 3; 3};
AC={9-3; 10-4; 11-5}={6; 6; 6}.
Sudauginę vektorius AB ir AC vektorine vektorių sandauga (cross product) gausime vektorių inNormal (gausime trikampio normalės vektorių).
Blogas pavyzdis, nes vektoriai AB ir AC lygiagretūs (normalizavus gaunasi tas pats vektorius). Visi trys taškai guli ant vienos linijos.
Duotas trikampis, kurio taškai (viršūnės) yra A(3; 4; 5), B(10; 13; 17), C(2; 6; 1). Rasime vektorius AB ir AC.
AB={10-3; 13-4; 17-5}={7; 9; 12};
AC={2-3; 6-4; 1-5}={-1; 2; -4}.
Sudauginę vektorius AB ir AC vektorine vektorių sandauga (cross product) gausime vektorių inNormal (gausime trikampio normalės vektorių).
Vektorius viewVec yra viewVec={5; 8; 4}. Rasime vektorių g (vektorius g guli ant trikampio ABC plokštumos, nes vektorius inNormal yra status trikampio plokštumai).
g=cross(viewVec, inNormal)=
Suprastinus vektorius g lygus g={24; -71; 112}.
Toliau rasime vektorių y.
y=cross(g, inNormal)=
Net iš normalizuotų vektorių vektorinė vektorių sandauga neduoda normalizuoto vektoriaus (jei duoti normalizuoti vektoriai a={0.8; 0.6; 0} ir b={-0.8; 0.6; 0}, tai z koordinatė vektoriaus c=cross(a, b) lygi k(0.8*0.6-(-0.8)*0.6)=k(0.48+0.48)=0.96 k, kas ne lygu 1 k). Todėl reikia normalizuoti vektorių y.
Normalizuosime vektorių viewVec={5; 8; 4}.
Toliau normalizuosime vektorių inNormal={-60; 16; 23}.
Padarę skaliarinę vektorių sandaugą gausimę kosinusą kampo tarp vektoriaus viewVec ir vektoriaus inNormal.
cosine = dot(viewVec, inNormal)=
Randame sinusą
sine = sqrt(1 - cosine * cosine) =
Jei kampas tarp dviejų vektorių 0 laipsnių, tai kosinusas lygus 1, o sinusas lygus 0. O jei kampas tarp dviejų vektorių lygus 90 laipsnių, tai kosinusas lygus 0, o sinusas lygus 1.
Funkcija saturate(x) yra ekvivalenti funkcijai clamp(0, 1, x). Funkcija clamp(0, 1, x) duoda reikšmes tik nuo 0 iki 1. Jei x daugiau už 1, tada vis tiek funkcija clamp(0, 1, x) grąžins 1.
Ir jeigu x mažiau už 0, tai vis tiek bus 0. Funkcija saturate duoda reikšmes intervale nuo 0 iki 1. O didesnias arba mažesnes reikšmes paverčia į 1 arba 0.
// Note that the saturate(x) function is equivalent to
// using clamp(0,1,x).
Jei šitą kodą
float3 x = -inNormal;
float3 y = normalize(cross(cross(viewVec, inNormal), inNormal));
Out.Refract = x * cosine2 + y * sine2;
pakeisti tokiu kodu
float3 x = -inNormal;
float3 y = normalize(cross(cross(viewVec, inNormal), inNormal));
Out.Refract = x * cosine + y * sine;
tai tada bus gautas viewVec arba -viewVec vektorius (sunku pasakyti ar reikalingas minusas ar ne). Tai yra
\\ Out.Refract = x * cosine + y * sine = viewVec
arba
\\ Out.Refract = x * cosine + y * sine = -viewVec
Vektorius x = -inNormal. O vektorius y yra projekcija vektoriaus viewVec (arba vektoriaus -viewVec, neaišku ar reikia to minuso) ant trikampio plokštumos.
Normalizuosime vektorių
Patikrinsime ar x * cosine + y * sine = viewVec.
x * cosine + y * sine =
Gavosi apytiksli -viewVec reikšmė. Tikroji viewVec reikšmė yra tokia Vektorius x*cosine+y*sine yra beveik normalizuotas: