mdx-m3-viewer
Version:
A browser WebGL model viewer. Mainly focused on models of the games Warcraft 3 and Starcraft 2.
472 lines (380 loc) • 14.3 kB
JavaScript
import shaders from '../../shaders';
let vsCommon = `
vec3 TBN(vec3 vector, vec3 tangent, vec3 binormal, vec3 normal) {
return vec3(dot(vector, tangent), dot(vector, binormal), dot(vector, normal));
}
vec4 decodeVector(vec4 v) {
return ((v / 255.0) * 2.0) - 1.0;
}
`;
let fsCommon = `
varying vec3 v_normal;
varying vec4 v_uv[2];
varying vec3 v_lightDir;
// varying vec3 v_eyeVec;
varying vec3 v_halfVec;
varying vec3 v_teamColor;
varying vec4 v_vertexColor;
struct LayerSettings {
bool enabled;
float op;
float channels;
float teamColorMode;
// vec3 multAddAlpha;
// bool useAlphaFactor;
bool invert;
// bool multColor;
// bool addColor;
bool clampResult;
// bool useConstantColor;
// vec4 constantColor;
// float uvSource;
float uvCoordinate;
// float fresnelMode;
// float fresnelTransformMode;
// mat4 fresnelTransform;
// bool fresnelClamp;
// vec3 fresnelExponentBiasScale;
};
// float calculateFresnelTerm(vec3 normal, vec3 eyeToVertex, float exponent, mat4 fresnelTransform, float fresnelTransformMode, bool fresnelClamp) {
// vec3 fresnelDir = eyeToVertex;
// float result;
// if (fresnelTransformMode != FRESNELTRANSFORM_NONE) {
// fresnelDir = (fresnelTransform * vec4(fresnelDir, 1.0)).xyz;
// if (fresnelTransformMode == FRESNELTRANSFORM_NORMALIZED) {
// fresnelDir = normalize(fresnelDir);
// }
// }
// if (fresnelClamp) {
// result = 1.0 - clamp(-dot(normal, fresnelDir), 0.0, 1.0);
// } else {
// result = 1.0 - abs(dot(normal, fresnelDir));
// }
// result = max(result, 0.0000001);
// return pow(result, exponent);
// }
vec3 combineLayerColor(vec4 color, vec3 result, LayerSettings layerSettings) {
if (layerSettings.op == LAYEROP_MOD) {
result *= color.rgb;
} else if (layerSettings.op == LAYEROP_MOD2X) {
result *= color.rgb * 2.0;
} else if (layerSettings.op == LAYEROP_ADD) {
result += color.rgb * color.a;
} else if (layerSettings.op == LAYEROP_ADD_NO_ALPHA) {
result += color.rgb;
} else if (layerSettings.op == LAYEROP_LERP) {
result = mix(result, color.rgb, color.a);
} else if (layerSettings.op == LAYEROP_TEAMCOLOR_EMISSIVE_ADD) {
result += color.a * v_teamColor;
} else if (layerSettings.op == LAYEROP_TEAMCOLOR_DIFFUSE_ADD) {
result += color.a * v_teamColor;
}
return result;
}
vec4 chooseChannel(float channel, vec4 texel) {
if (channel == CHANNELSELECT_R) {
texel = texel.rrrr;
} else if (channel == CHANNELSELECT_G) {
texel = texel.gggg;
} else if (channel == CHANNELSELECT_B) {
texel = texel.bbbb;
} else if (channel == CHANNELSELECT_A) {
texel = texel.aaaa;
} else if (channel == CHANNELSELECT_RGB) {
texel.a = 1.0;
}
return texel;
}
vec2 getUV(LayerSettings layerSettings) {
if (layerSettings.uvCoordinate == 1.0) {
return v_uv[0].zw;
} else if (layerSettings.uvCoordinate == 2.0) {
return v_uv[1].xy;
} else if (layerSettings.uvCoordinate == 3.0) {
return v_uv[1].zw;
}
return v_uv[0].xy;
}
vec4 sampleLayer(sampler2D layer, LayerSettings layerSettings) {
// if (layerSettings.useConstantColor) {
// return layerSettings.constantColor;
// }
return texture2D(layer, getUV(layerSettings));
}
vec4 computeLayerColor(sampler2D layer, LayerSettings layerSettings) {
vec4 color = sampleLayer(layer, layerSettings);
// if (layerSettings.useMask) {
// result *= mask;
// }
vec4 result = chooseChannel(layerSettings.channels, color);
// if (layerSettings.useAlphaFactor) {
// result.a *= layerSettings.multiplyAddAlpha.z;
// }
if (layerSettings.teamColorMode == TEAMCOLOR_DIFFUSE) {
result = vec4(mix(v_teamColor, result.rgb, color.a), 1.0);
} else if (layerSettings.teamColorMode == TEAMCOLOR_EMISSIVE) {
result = vec4(mix(v_teamColor, result.rgb, color.a), 1.0);
}
if (layerSettings.invert) {
result = vec4(1.0) - result;
}
// if (layerSettings.multiplyEnable) {
// result *= layerSettings.multiplyAddAlpha.x;
// }
// if (layerSettings.addEnable) {
// result += layerSettings.multiplyAddAlpha.y;
// }
if (layerSettings.clampResult) {
result = clamp(result, 0.0, 1.0);
}
// if (layerSettings.fresnelMode != FRESNELMODE_NONE) {
// float fresnelTerm = calculateFresnelTerm(v_normal, v_eyeVec, layerSettings.fresnelExponentBiasScale.x, layerSettings.fresnelTransform, layerSettings.fresnelTransformMode, layerSettings.fresnelClamp);
// if (layerSettings.fresnelMode == FRESNELMODE_INVERTED) {
// fresnelTerm = 1.0 - fresnelTerm;
// }
// fresnelTerm = clamp(fresnelTerm * layerSettings.fresnelExponentBiasScale.z + layerSettings.fresnelExponentBiasScale.y, 0.0, 1.0);
// result *= fresnelTerm;
// }
return result;
}
vec3 decodeNormal(sampler2D map) {
vec4 texel = texture2D(map, v_uv[0].xy);
vec3 normal;
normal.xy = 2.0 * texel.wy - 1.0;
normal.z = sqrt(max(0.0, 1.0 - dot(normal.xy, normal.xy)));
return normal;
}
vec4 computeSpecular(sampler2D specularMap, LayerSettings layerSettings, float specularity, float specMult, vec3 normal) {
vec4 color;
if (layerSettings.enabled) {
color = computeLayerColor(specularMap, layerSettings);
} else {
color = vec4(0);
}
float factor = pow(max(-dot(v_halfVec, normal), 0.0), specularity) * specMult;
return color * factor;
}
`;
export default {
vs: `
${shaders.instanceId}
${shaders.boneTexture}
${vsCommon}
uniform mat4 u_mvp;
uniform mat4 u_mv;
uniform vec3 u_eyePos;
uniform vec3 u_lightPos;
uniform float u_firstBoneLookupIndex;
uniform float u_boneWeightPairsCount;
uniform vec3 u_teamColors[14];
attribute vec3 a_position;
attribute vec4 a_normal;
attribute vec2 a_uv0;
attribute vec2 a_uv1;
attribute vec2 a_uv1, a_uv2;
attribute vec2 a_uv1, a_uv2, a_uv3;
attribute vec4 a_tangent;
attribute vec4 a_bones;
attribute vec4 a_weights;
attribute float a_teamColor;
attribute vec4 a_vertexColor;
varying vec3 v_normal;
varying vec4 v_uv[2]; // Pack 4 vec2 in 2 vec4, to reduce the varyings count
varying vec3 v_lightDir;
// varying vec3 v_eyeVec;
varying vec3 v_halfVec;
varying vec3 v_teamColor;
varying vec4 v_vertexColor;
void transform(inout vec3 position, inout vec3 normal, inout vec3 tangent, inout vec3 binormal, vec4 bones, vec4 weights) {
if (u_boneWeightPairsCount > 0.0) {
mat4 bone;
if (u_boneWeightPairsCount == 1.0) {
bone = fetchMatrix(bones[0], a_InstanceID);
} else {
bone += fetchMatrix(bones[0], a_InstanceID) * weights[0];
bone += fetchMatrix(bones[1], a_InstanceID) * weights[1];
bone += fetchMatrix(bones[2], a_InstanceID) * weights[2];
bone += fetchMatrix(bones[3], a_InstanceID) * weights[3];
}
position = vec3(bone * vec4(position, 1.0));
normal = mat3(bone) * normal;
tangent = vec3(bone * vec4(tangent, 0.0));
binormal = vec3(bone * vec4(binormal, 0.0));
}
}
void main() {
vec4 decodedNormal = decodeVector(a_normal);
vec3 position = a_position;
vec3 normal = decodedNormal.xyz;
vec3 tangent = vec3(decodeVector(a_tangent));
vec3 binormal = cross(normal, tangent) * decodedNormal.w;
transform(position, normal, tangent, binormal, a_bones + u_firstBoneLookupIndex, a_weights / 255.0);
vec3 position_mv = vec3(u_mv * vec4(position, 1));
mat3 mv = mat3(u_mv);
vec3 t = normalize(mv * tangent);
vec3 b = normalize(mv * binormal);
vec3 n = normalize(mv * normal);
vec3 lightDir = normalize(u_lightPos - position_mv);
v_lightDir = normalize(TBN(lightDir, t, b, n));
vec3 eyeVec = normalize(u_eyePos - position_mv);
vec3 halfVec = normalize(eyeVec - u_lightPos);
// v_eyeVec = TBN(eyeVec, t, b, n);
v_halfVec = TBN(halfVec, t, b, n);
v_normal = n;
vec4 uv0, uv1;
uv0.xy = a_uv0 / 2048.0;
uv0.zw = a_uv1 / 2048.0;
uv1.xy = a_uv2 / 2048.0;
uv1.zw = a_uv3 / 2048.0;
v_uv[0] = uv0;
v_uv[1] = uv1;
v_teamColor = u_teamColors[int(a_teamColor)];
v_vertexColor = a_vertexColor;
gl_Position = u_mvp * vec4(position, 1.0);
}
`,
fs: `
${fsCommon}
uniform float u_specularity;
uniform float u_specMult;
uniform float u_emisMult;
uniform vec4 u_lightAmbient;
uniform LayerSettings u_diffuseLayerSettings;
uniform sampler2D u_diffuseMap;
uniform LayerSettings u_decalLayerSettings;
uniform sampler2D u_decalMap;
uniform LayerSettings u_specularLayerSettings;
uniform sampler2D u_specularMap;
uniform LayerSettings u_glossLayerSettings;
uniform sampler2D u_glossMap;
uniform LayerSettings u_emissiveLayerSettings;
uniform sampler2D u_emissiveMap;
uniform LayerSettings u_emissive2LayerSettings;
uniform sampler2D u_emissive2Map;
uniform LayerSettings u_evioLayerSettings;
uniform sampler2D u_evioMap;
uniform LayerSettings u_evioMaskLayerSettings;
uniform sampler2D u_evioMaskMap;
uniform LayerSettings u_alphaLayerSettings;
uniform sampler2D u_alphaMap;
uniform LayerSettings u_alphaMaskLayerSettings;
uniform sampler2D u_alphaMaskMap;
uniform LayerSettings u_normalLayerSettings;
uniform sampler2D u_normalMap;
uniform LayerSettings u_heightLayerSettings;
uniform sampler2D u_heightMap;
uniform LayerSettings u_lightMapLayerSettings;
uniform sampler2D u_lightMapMap;
uniform LayerSettings u_aoLayerSettings;
uniform sampler2D u_aoMap;
void main() {
vec3 color;
vec4 final = u_lightAmbient;
vec3 normal;
vec3 lightMapDiffuse;
if (u_normalLayerSettings.enabled) {
normal = decodeNormal(u_normalMap);
} else {
normal = v_normal;
}
float lambertFactor = max(dot(normal, v_lightDir), 0.0);
if (lambertFactor > 0.0) {
if (u_diffuseLayerSettings.enabled) {
vec4 diffuseColor = computeLayerColor(u_diffuseMap, u_diffuseLayerSettings);
color = combineLayerColor(diffuseColor, color, u_diffuseLayerSettings);
}
if (u_decalLayerSettings.enabled) {
vec4 decalColor = computeLayerColor(u_decalMap, u_decalLayerSettings);
color = combineLayerColor(decalColor, color, u_decalLayerSettings);
}
vec4 specularColor = computeSpecular(u_specularMap, u_specularLayerSettings, u_specularity, u_specMult, normal);
if (u_lightMapLayerSettings.enabled) {
vec4 lightMapColor = computeLayerColor(u_lightMapMap, u_lightMapLayerSettings) * 2.0;
lightMapDiffuse = lightMapColor.rgb;
}
// final.rgb = color * lightMapDiffuse + specularColor.rgb;
final.rgb = (color + specularColor.rgb) * lambertFactor;
bool addEmissive = false;
vec3 emissiveColor;
vec4 tempColor;
if (u_emissiveLayerSettings.enabled) {
tempColor = computeLayerColor(u_emissiveMap, u_emissiveLayerSettings);
if (u_emissiveLayerSettings.op == LAYEROP_MOD || u_emissiveLayerSettings.op == LAYEROP_MOD2X || u_emissiveLayerSettings.op == LAYEROP_LERP) {
final.rgb = combineLayerColor(tempColor, final.rgb, u_emissiveLayerSettings);
} else {
emissiveColor = combineLayerColor(tempColor, emissiveColor, u_emissiveLayerSettings);
addEmissive = true;
}
}
if (u_emissive2LayerSettings.enabled) {
tempColor = computeLayerColor(u_emissive2Map, u_emissive2LayerSettings);
if (!addEmissive && (u_emissive2LayerSettings.op == LAYEROP_MOD || u_emissive2LayerSettings.op == LAYEROP_MOD2X || u_emissive2LayerSettings.op == LAYEROP_LERP)) {
final.rgb = combineLayerColor(tempColor, final.rgb, u_emissive2LayerSettings);
} else {
emissiveColor = combineLayerColor(tempColor, emissiveColor, u_emissive2LayerSettings);
addEmissive = true;
}
}
if (addEmissive) {
final.rgb += emissiveColor * u_emisMult;
}
}
gl_FragColor = final * v_vertexColor;
}
`,
};