/*==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 . 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; iIlluminate(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&&diffCoef0.0f)) R = sc.ReflectVector(); for (int i=0; iIlluminate(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; iIlluminate(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&&diffCoef0.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; iIlluminate(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 = (NV0.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); }