You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
285 lines
9.1 KiB
285 lines
9.1 KiB
/*==LICENSE==* |
|
|
|
CyanWorlds.com Engine - MMOG client, server and tools |
|
Copyright (C) 2011 Cyan Worlds, Inc. |
|
|
|
This program is free software: you can redistribute it and/or modify |
|
it under the terms of the GNU General Public License as published by |
|
the Free Software Foundation, either version 3 of the License, or |
|
(at your option) any later version. |
|
|
|
This program is distributed in the hope that it will be useful, |
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
GNU General Public License for more details. |
|
|
|
You should have received a copy of the GNU General Public License |
|
along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
|
|
You can contact Cyan Worlds, Inc. by email legal@cyan.com |
|
or by snail mail at: |
|
Cyan Worlds, Inc. |
|
14617 N Newport Hwy |
|
Mead, WA 99021 |
|
|
|
*==LICENSE==*/ |
|
#include "hsTypes.h" |
|
#include "max.h" |
|
//#include "mtlhdr.h" |
|
#include "Shaders.h" |
|
|
|
//=========================================================================== |
|
// Useful variables... |
|
//=========================================================================== |
|
|
|
static PhongShader phongShader; |
|
static BlinnShader blinnShader; |
|
static MetalShader metalShader; |
|
static hsMaxShader plasmaShader; |
|
|
|
static Shader *shaders[3] = { |
|
&phongShader, |
|
&metalShader, |
|
&blinnShader, |
|
}; |
|
|
|
AColor black(0.0f,0.0f,0.0f,0.0f); |
|
|
|
//=========================================================================== |
|
// Useful functions... |
|
//=========================================================================== |
|
|
|
Shader *GetShader(int s) { return shaders[s]; }; |
|
|
|
#if 1 |
|
// Quadratic |
|
static inline float Soften(float r) { |
|
return r*(2.0f-r); |
|
} |
|
#else |
|
// Cubic |
|
static inline float Soften(float r) { |
|
return r*r*(3.0f-2.0f*r); |
|
} |
|
#endif |
|
|
|
float CompK(float f0) { |
|
return float(2.0*sqrt(f0)/sqrt(1.0-f0)); |
|
} |
|
|
|
float fres_metal(float c, float k) { |
|
float b,rpl, rpp,c2; |
|
b = k*k + 1.0f; |
|
c2 = c*c; |
|
rpl = (b*c2-2*c+1)/(b*c2+2*c+1); |
|
rpp = (b-2*c+c2)/(b+2*c+c2); |
|
return (.5f*(rpl+rpp)); |
|
} |
|
|
|
//=========================================================================== |
|
// Phong shader... don't know if this is ever going to be used |
|
//=========================================================================== |
|
|
|
void PhongShader::Illum(ShadeContext &sc, SIllumParams &ip) { |
|
LightDesc *l; |
|
Color lightCol; |
|
BOOL is_shiny; |
|
Point3 R; |
|
if (is_shiny=(ip.sh_str>0.0f)) |
|
R = sc.ReflectVector(); |
|
|
|
for (int i=0; i<sc.nLights; i++) { |
|
l = sc.Light(i); |
|
register float NL, diffCoef; |
|
Point3 L; |
|
if (l->Illuminate(sc,ip.N,lightCol,L,NL,diffCoef)) { |
|
// diffuse |
|
if (NL<=0.0f) |
|
continue; |
|
if (l->affectDiffuse) |
|
ip.diffIllum += diffCoef*lightCol; |
|
if (is_shiny&&l->affectSpecular) { |
|
// specular (Phong) |
|
float c = DotProd(L,R); |
|
if (c>0.0f) { |
|
if (ip.softThresh!=0.0&&diffCoef<ip.softThresh) { |
|
float r = diffCoef/ip.softThresh; |
|
c *= Soften(r); |
|
} |
|
c = (float)pow((double)c, (double)ip.ph_exp); // could use table lookup for speed |
|
ip.specIllum += c*ip.sh_str*lightCol; |
|
} |
|
} |
|
} |
|
} |
|
ip.specIllum *= ip.spec; |
|
} |
|
|
|
//=========================================================================== |
|
// hsMax shader... write this? |
|
//=========================================================================== |
|
|
|
void hsMaxShader::Illum(ShadeContext &sc, SIllumParams &ip) { |
|
LightDesc *l; |
|
Color lightCol; |
|
BOOL is_shiny; |
|
Point3 R; |
|
if (is_shiny=(ip.sh_str>0.0f)) |
|
R = sc.ReflectVector(); |
|
|
|
for (int i=0; i<sc.nLights; i++) { |
|
l = sc.Light(i); |
|
register float NL, diffCoef; |
|
Point3 L; |
|
if (l->Illuminate(sc,ip.N,lightCol,L,NL,diffCoef)) { |
|
// diffuse |
|
if (NL<=0.0f) |
|
continue; |
|
if (l->affectDiffuse) |
|
ip.diffIllum += diffCoef*lightCol; |
|
if (is_shiny&&l->affectSpecular) { |
|
// specular |
|
float c = DotProd(L,R); |
|
if (c>0.0f) { |
|
c = (float)pow((double)c, (double)ip.ph_exp); // could use table lookup for speed |
|
ip.specIllum += c*ip.sh_str*lightCol; |
|
} |
|
} |
|
} |
|
} |
|
ip.specIllum *= ip.spec; |
|
} |
|
|
|
//=========================================================================== |
|
// Blinn shader... don't know if this is ever going to be used |
|
//=========================================================================== |
|
|
|
void BlinnShader::Illum(ShadeContext &sc, SIllumParams &ip) { |
|
LightDesc *l; |
|
Color lightCol; |
|
|
|
// Blinn style phong |
|
BOOL is_shiny=(ip.sh_str>0.0f)?1:0; |
|
double ph_exp = double(ip.ph_exp)*4.0; // This is to make the hilite compatible with normal phong |
|
for (int i=0; i<sc.nLights; i++) { |
|
l = sc.Light(i); |
|
register float NL, diffCoef; |
|
Point3 L; |
|
if (l->Illuminate(sc,ip.N,lightCol,L,NL,diffCoef)) { |
|
// diffuse |
|
if (NL<=0.0f) |
|
continue; |
|
|
|
if (l->affectDiffuse) |
|
ip.diffIllum += diffCoef*lightCol; |
|
|
|
// specular (Phong) |
|
if (is_shiny&&l->affectSpecular) { |
|
Point3 H = FNormalize(L-ip.V); |
|
float c = DotProd(ip.N,H); |
|
if (c>0.0f) { |
|
if (ip.softThresh!=0.0&&diffCoef<ip.softThresh) { |
|
c *= Soften(diffCoef/ip.softThresh); |
|
} |
|
c = (float)pow((double)c, ph_exp); // could use table lookup for speed |
|
ip.specIllum += c*ip.sh_str*lightCol; |
|
} |
|
} |
|
} |
|
} |
|
ip.specIllum *= ip.spec; |
|
} |
|
|
|
//=========================================================================== |
|
// Metal shader... don't know if this is ever going to be used |
|
//=========================================================================== |
|
|
|
void MetalShader::Illum(ShadeContext &sc, SIllumParams &ip) { |
|
LightDesc *l; |
|
Color lightCol; |
|
BOOL gotKav = FALSE; |
|
float kav, fav0, m2inv,NV; |
|
|
|
//IPoint2 sp = sc.ScreenCoord(); |
|
|
|
BOOL is_shiny; |
|
if (ip.sh_str>0.0f) { |
|
NV = -DotProd(ip.N,ip.V); // N dot V: view vector is TOWARDS us. |
|
is_shiny = 1; |
|
float r = 1.0f-ip.shine; |
|
if (r==0.0f) r = .00001f; |
|
m2inv = 1.0f/(r*r); |
|
} |
|
else |
|
is_shiny = 0; |
|
|
|
|
|
for (int i=0; i<sc.nLights; i++) { |
|
l = sc.Light(i); |
|
register float NL, diffCoef; |
|
Point3 L; |
|
|
|
if (!l->Illuminate(sc,ip.N,lightCol,L,NL,diffCoef)) |
|
continue; |
|
|
|
// diffuse |
|
if (NL>0.0f&&l->affectDiffuse) // TBD is the NL test necessary? |
|
ip.diffIllum += diffCoef*lightCol; |
|
|
|
if (is_shiny&&l->affectSpecular) { // SPECULAR |
|
Color fcol; |
|
float LH,NH,VH; |
|
float sec2; // Was double?? TBD |
|
Point3 H; |
|
|
|
if (NV<0.0f) continue; |
|
|
|
H = FNormalize(L-ip.V); |
|
|
|
LH = DotProd(L,H); // cos(phi) |
|
NH = DotProd(ip.N,H); // cos(alpha) |
|
if (NH==0.0f) continue; |
|
VH = -DotProd(ip.V,H); |
|
|
|
// compute geometrical attenuation factor |
|
float G = (NV<NL)? (2.0f*NV*NH/VH): (2.0f*NL*NH/VH); |
|
if (G>0.0f) { |
|
// Compute (approximate) indices of refraction |
|
// this can be factored out for non-texture-mapped mtls |
|
if (!gotKav) { |
|
fav0 = Intens(ip.diff); |
|
if (fav0>=1.0f) fav0 = .9999f; |
|
kav = CompK(fav0); |
|
gotKav = TRUE; |
|
} |
|
|
|
float fav = fres_metal(LH,kav); |
|
float t = (fav-fav0)/(1.0f-fav0); |
|
fcol = (1.0f-t)*ip.diff + Color(t,t,t); |
|
|
|
// Beckman distribution (from Cook-Torrance paper) |
|
sec2 = 1.0f/(NH*NH); // 1/sqr(cos) |
|
float D = (.5f/PI)*sec2*sec2*m2inv*(float)exp((1.0f-sec2)*m2inv); |
|
if (G>1.0f) G = 1.0f; |
|
float Rs = ip.sh_str*D*G/(NV+.05f); |
|
ip.specIllum += fcol*Rs*lightCol; |
|
} |
|
} |
|
} |
|
ip.diffIllum *= 1.0f - ip.sh_str; |
|
} |
|
|
|
|
|
void MetalShader::SetShininess(float shininess, float shineStr) { |
|
float r = 1.0f-shininess; |
|
if (r==0.0f) r = .00001f; |
|
fm2inv = 1.0f/(r*r); |
|
fshin_str = shineStr; |
|
} |
|
|
|
float MetalShader::EvalHilite(float x) { |
|
float c = (float)cos(x*PI); |
|
float sec2 = 1.0f/(c*c); /* 1/sqr(cos) */ |
|
return fshin_str*(.5f/PI)*sec2*sec2*fm2inv*(float)exp((1.0f-sec2)*fm2inv); |
|
} |
|
|
|
|