We have a limited number of interpolants, and 2 were used for spotlights, but this is a fairly uncommon case, so we move the calculation to the fragment shader. A side effect of this is that we are not limited to two spot-light anymore from the shader's point of view. This essentially adds a vector transform per fragment, but on the other hand, it removes communication between the vertex and fragment shader, as well as interpolation of the position.
154 lines
4.6 KiB
GLSL
154 lines
4.6 KiB
GLSL
//------------------------------------------------------------------------------
|
|
// Uniforms access
|
|
//------------------------------------------------------------------------------
|
|
|
|
mat4 getLightFromWorldMatrix() {
|
|
return frameUniforms.lightFromWorldMatrix[0];
|
|
}
|
|
|
|
/** @public-api */
|
|
mat4 getWorldFromModelMatrix() {
|
|
return objectUniforms.worldFromModelMatrix;
|
|
}
|
|
|
|
/** @public-api */
|
|
mat3 getWorldFromModelNormalMatrix() {
|
|
return objectUniforms.worldFromModelNormalMatrix;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Attributes access
|
|
//------------------------------------------------------------------------------
|
|
|
|
#if defined(HAS_SKINNING_OR_MORPHING)
|
|
vec3 mulBoneNormal(vec3 n, uint i) {
|
|
vec4 q = bonesUniforms.bones[i + 0u];
|
|
vec3 is = bonesUniforms.bones[i + 3u].xyz;
|
|
|
|
// apply the inverse of the non-uniform scales
|
|
n *= is;
|
|
// apply the rigid transform (valid only for unit quaternions)
|
|
n += 2.0 * cross(q.xyz, cross(q.xyz, n) + q.w * n);
|
|
|
|
return n;
|
|
}
|
|
|
|
vec3 mulBoneVertex(vec3 v, uint i) {
|
|
vec4 q = bonesUniforms.bones[i + 0u];
|
|
vec3 t = bonesUniforms.bones[i + 1u].xyz;
|
|
vec3 s = bonesUniforms.bones[i + 2u].xyz;
|
|
|
|
// apply the non-uniform scales
|
|
v *= s;
|
|
// apply the rigid transform (valid only for unit quaternions)
|
|
v += 2.0 * cross(q.xyz, cross(q.xyz, v) + q.w * v);
|
|
// apply the translation
|
|
v += t;
|
|
|
|
return v;
|
|
}
|
|
|
|
void skinNormal(inout vec3 n, const uvec4 ids, const vec4 weights) {
|
|
n = mulBoneNormal(n, ids.x * 4u) * weights.x
|
|
+ mulBoneNormal(n, ids.y * 4u) * weights.y
|
|
+ mulBoneNormal(n, ids.z * 4u) * weights.z
|
|
+ mulBoneNormal(n, ids.w * 4u) * weights.w;
|
|
}
|
|
|
|
void skinPosition(inout vec3 p, const uvec4 ids, const vec4 weights) {
|
|
p = mulBoneVertex(p, ids.x * 4u) * weights.x
|
|
+ mulBoneVertex(p, ids.y * 4u) * weights.y
|
|
+ mulBoneVertex(p, ids.z * 4u) * weights.z
|
|
+ mulBoneVertex(p, ids.w * 4u) * weights.w;
|
|
}
|
|
#endif
|
|
|
|
/** @public-api */
|
|
vec4 getPosition() {
|
|
vec4 pos = mesh_position;
|
|
|
|
#if defined(HAS_SKINNING_OR_MORPHING)
|
|
|
|
if (objectUniforms.morphingEnabled == 1) {
|
|
pos += objectUniforms.morphWeights.x * mesh_custom0;
|
|
pos += objectUniforms.morphWeights.y * mesh_custom1;
|
|
pos += objectUniforms.morphWeights.z * mesh_custom2;
|
|
pos += objectUniforms.morphWeights.w * mesh_custom3;
|
|
}
|
|
|
|
if (objectUniforms.skinningEnabled == 1) {
|
|
skinPosition(pos.xyz, mesh_bone_indices, mesh_bone_weights);
|
|
}
|
|
|
|
#endif
|
|
|
|
return pos;
|
|
}
|
|
|
|
#if defined(HAS_ATTRIBUTE_CUSTOM0)
|
|
vec4 getCustom0() { return mesh_custom0; }
|
|
#endif
|
|
#if defined(HAS_ATTRIBUTE_CUSTOM1)
|
|
vec4 getCustom1() { return mesh_custom1; }
|
|
#endif
|
|
#if defined(HAS_ATTRIBUTE_CUSTOM2)
|
|
vec4 getCustom2() { return mesh_custom2; }
|
|
#endif
|
|
#if defined(HAS_ATTRIBUTE_CUSTOM3)
|
|
vec4 getCustom3() { return mesh_custom3; }
|
|
#endif
|
|
#if defined(HAS_ATTRIBUTE_CUSTOM4)
|
|
vec4 getCustom4() { return mesh_custom4; }
|
|
#endif
|
|
#if defined(HAS_ATTRIBUTE_CUSTOM5)
|
|
vec4 getCustom5() { return mesh_custom5; }
|
|
#endif
|
|
#if defined(HAS_ATTRIBUTE_CUSTOM6)
|
|
vec4 getCustom6() { return mesh_custom6; }
|
|
#endif
|
|
#if defined(HAS_ATTRIBUTE_CUSTOM7)
|
|
vec4 getCustom7() { return mesh_custom7; }
|
|
#endif
|
|
|
|
/** @public-api */
|
|
int getVertexIndex() {
|
|
#if defined(TARGET_METAL_ENVIRONMENT) || defined(TARGET_VULKAN_ENVIRONMENT)
|
|
return gl_VertexIndex;
|
|
#else
|
|
return gl_VertexID;
|
|
#endif
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Helpers
|
|
//------------------------------------------------------------------------------
|
|
|
|
/**
|
|
* Computes and returns the position in world space of the current vertex.
|
|
* The world position computation depends on the current vertex domain. This
|
|
* function optionally applies vertex skinning if needed.
|
|
*
|
|
* NOTE: the "transform" and "position" temporaries are necessary to work around
|
|
* an issue with Adreno drivers (b/110851741).
|
|
*/
|
|
vec4 computeWorldPosition() {
|
|
#if defined(VERTEX_DOMAIN_OBJECT)
|
|
mat4 transform = getWorldFromModelMatrix();
|
|
vec3 position = getPosition().xyz;
|
|
return mulMat4x4Float3(transform, position);
|
|
#elif defined(VERTEX_DOMAIN_WORLD)
|
|
return vec4(getPosition().xyz, 1.0);
|
|
#elif defined(VERTEX_DOMAIN_VIEW)
|
|
mat4 transform = getWorldFromViewMatrix();
|
|
vec3 position = getPosition().xyz;
|
|
return mulMat4x4Float3(transform, position);
|
|
#else
|
|
mat4 transform = getWorldFromClipMatrix();
|
|
vec4 position = transform * getPosition();
|
|
if (abs(position.w) < MEDIUMP_FLT_MIN) {
|
|
position.w = position.w < 0.0 ? -MEDIUMP_FLT_MIN : MEDIUMP_FLT_MIN;
|
|
}
|
|
return position * (1.0 / position.w);
|
|
#endif
|
|
}
|