Five custom variables (varyings) are now available on the condition that the `color` attribute is not requested. FIXES=[404930099]
264 lines
11 KiB
GLSL
264 lines
11 KiB
GLSL
/*
|
|
* This is the main vertex shader of surface materials. It can be invoked with
|
|
* USE_OPTIMIZED_DEPTH_VERTEX_SHADER defined, and in this case we are guaranteed that the
|
|
* DEPTH variant is active *AND* there is no custom vertex shader (i.e.: materialVertex() is
|
|
* empty).
|
|
* We can use this to remove all code that doesn't participate in the depth computation.
|
|
*/
|
|
|
|
void main() {
|
|
#if defined(FILAMENT_HAS_FEATURE_INSTANCING)
|
|
# if defined(TARGET_METAL_ENVIRONMENT) || defined(TARGET_VULKAN_ENVIRONMENT)
|
|
instance_index = gl_InstanceIndex;
|
|
# else
|
|
// PowerVR drivers don't initialize gl_InstanceID correctly if it's assigned to the varying
|
|
// directly and early in the shader. Adding a bit of extra integer math, works around it.
|
|
// Using an intermediate variable doesn't work because of spirv-opt.
|
|
if (CONFIG_POWER_VR_SHADER_WORKAROUNDS) {
|
|
instance_index = (1 + gl_InstanceID) - 1;
|
|
} else {
|
|
instance_index = gl_InstanceID;
|
|
}
|
|
# endif
|
|
logical_instance_index = instance_index;
|
|
#endif
|
|
|
|
#if defined(VARIANT_HAS_STEREO) && defined(FILAMENT_STEREO_INSTANCED)
|
|
#if !defined(FILAMENT_HAS_FEATURE_INSTANCING)
|
|
#error Instanced stereo not supported at this feature level
|
|
#endif
|
|
// Calculate the logical instance index, which is the instance index within a single eye.
|
|
logical_instance_index = instance_index / CONFIG_STEREO_EYE_COUNT;
|
|
#endif
|
|
|
|
initObjectUniforms();
|
|
|
|
// Initialize the inputs to sensible default values, see surface_material_inputs.vs
|
|
#if defined(USE_OPTIMIZED_DEPTH_VERTEX_SHADER)
|
|
|
|
// In USE_OPTIMIZED_DEPTH_VERTEX_SHADER mode, we can even skip this if we're already in
|
|
// VERTEX_DOMAIN_DEVICE and we don't have VSM.
|
|
#if !defined(VERTEX_DOMAIN_DEVICE) || defined(VARIANT_HAS_VSM)
|
|
// Run initMaterialVertex to compute material.worldPosition.
|
|
MaterialVertexInputs material;
|
|
initMaterialVertex(material);
|
|
// materialVertex() is guaranteed to be empty here, but we keep it to workaround some problem
|
|
// in NVIDA drivers related to depth invariance.
|
|
materialVertex(material);
|
|
#endif
|
|
|
|
#else // defined(USE_OPTIMIZED_DEPTH_VERTEX_SHADER)
|
|
|
|
MaterialVertexInputs material;
|
|
initMaterialVertex(material);
|
|
|
|
#if defined(HAS_ATTRIBUTE_TANGENTS)
|
|
// If the material defines a value for the "normal" property, we need to output
|
|
// the full orthonormal basis to apply normal mapping
|
|
#if defined(MATERIAL_NEEDS_TBN)
|
|
// Extract the normal and tangent in world space from the input quaternion
|
|
// We encode the orthonormal basis as a quaternion to save space in the attributes
|
|
toTangentFrame(mesh_tangents, material.worldNormal, vertex_worldTangent.xyz);
|
|
|
|
#if defined(VARIANT_HAS_SKINNING_OR_MORPHING)
|
|
if ((object_uniforms_flagsChannels & FILAMENT_OBJECT_MORPHING_ENABLED_BIT) != 0) {
|
|
#if defined(LEGACY_MORPHING)
|
|
vec3 normal0, normal1, normal2, normal3;
|
|
toTangentFrame(mesh_custom4, normal0);
|
|
toTangentFrame(mesh_custom5, normal1);
|
|
toTangentFrame(mesh_custom6, normal2);
|
|
toTangentFrame(mesh_custom7, normal3);
|
|
vec3 baseNormal = material.worldNormal;
|
|
material.worldNormal += morphingUniforms.weights[0].xyz * (normal0 - baseNormal);
|
|
material.worldNormal += morphingUniforms.weights[1].xyz * (normal1 - baseNormal);
|
|
material.worldNormal += morphingUniforms.weights[2].xyz * (normal2 - baseNormal);
|
|
material.worldNormal += morphingUniforms.weights[3].xyz * (normal3 - baseNormal);
|
|
#else
|
|
morphNormal(material.worldNormal);
|
|
material.worldNormal = normalize(material.worldNormal);
|
|
#endif
|
|
}
|
|
|
|
if ((object_uniforms_flagsChannels & FILAMENT_OBJECT_SKINNING_ENABLED_BIT) != 0) {
|
|
skinNormalTangent(material.worldNormal, vertex_worldTangent.xyz, mesh_bone_indices, mesh_bone_weights);
|
|
}
|
|
#endif
|
|
|
|
// We don't need to normalize here, even if there's a scale in the matrix
|
|
// because we ensure the worldFromModelNormalMatrix pre-scales the normal such that
|
|
// all its components are < 1.0. This prevents the bitangent to exceed the range of fp16
|
|
// in the fragment shader, where we renormalize after interpolation
|
|
vertex_worldTangent.xyz = getWorldFromModelNormalMatrix() * vertex_worldTangent.xyz;
|
|
vertex_worldTangent.w = mesh_tangents.w;
|
|
material.worldNormal = getWorldFromModelNormalMatrix() * material.worldNormal;
|
|
#else // MATERIAL_NEEDS_TBN
|
|
// Without anisotropy or normal mapping we only need the normal vector
|
|
toTangentFrame(mesh_tangents, material.worldNormal);
|
|
|
|
#if defined(VARIANT_HAS_SKINNING_OR_MORPHING)
|
|
if ((object_uniforms_flagsChannels & FILAMENT_OBJECT_MORPHING_ENABLED_BIT) != 0) {
|
|
#if defined(LEGACY_MORPHING)
|
|
vec3 normal0, normal1, normal2, normal3;
|
|
toTangentFrame(mesh_custom4, normal0);
|
|
toTangentFrame(mesh_custom5, normal1);
|
|
toTangentFrame(mesh_custom6, normal2);
|
|
toTangentFrame(mesh_custom7, normal3);
|
|
vec3 baseNormal = material.worldNormal;
|
|
material.worldNormal += morphingUniforms.weights[0].xyz * (normal0 - baseNormal);
|
|
material.worldNormal += morphingUniforms.weights[1].xyz * (normal1 - baseNormal);
|
|
material.worldNormal += morphingUniforms.weights[2].xyz * (normal2 - baseNormal);
|
|
material.worldNormal += morphingUniforms.weights[3].xyz * (normal3 - baseNormal);
|
|
#else
|
|
morphNormal(material.worldNormal);
|
|
material.worldNormal = normalize(material.worldNormal);
|
|
#endif
|
|
}
|
|
|
|
if ((object_uniforms_flagsChannels & FILAMENT_OBJECT_SKINNING_ENABLED_BIT) != 0) {
|
|
skinNormal(material.worldNormal, mesh_bone_indices, mesh_bone_weights);
|
|
}
|
|
#endif
|
|
|
|
material.worldNormal = getWorldFromModelNormalMatrix() * material.worldNormal;
|
|
|
|
#endif // MATERIAL_HAS_ANISOTROPY || MATERIAL_HAS_NORMAL || MATERIAL_HAS_CLEAR_COAT_NORMAL
|
|
#endif // HAS_ATTRIBUTE_TANGENTS
|
|
|
|
// Invoke user code
|
|
materialVertex(material);
|
|
|
|
// Handle built-in interpolated attributes
|
|
#if defined(HAS_ATTRIBUTE_COLOR)
|
|
vertex_color = material.color;
|
|
#endif
|
|
#if defined(HAS_ATTRIBUTE_UV0)
|
|
vertex_uv01.xy = material.uv0;
|
|
#endif
|
|
#if defined(HAS_ATTRIBUTE_UV1)
|
|
vertex_uv01.zw = material.uv1;
|
|
#endif
|
|
|
|
// Handle user-defined interpolated attributes
|
|
#if defined(VARIABLE_CUSTOM0)
|
|
VARIABLE_CUSTOM_AT0 = material.VARIABLE_CUSTOM0;
|
|
#endif
|
|
#if defined(VARIABLE_CUSTOM1)
|
|
VARIABLE_CUSTOM_AT1 = material.VARIABLE_CUSTOM1;
|
|
#endif
|
|
#if defined(VARIABLE_CUSTOM2)
|
|
VARIABLE_CUSTOM_AT2 = material.VARIABLE_CUSTOM2;
|
|
#endif
|
|
#if defined(VARIABLE_CUSTOM3)
|
|
VARIABLE_CUSTOM_AT3 = material.VARIABLE_CUSTOM3;
|
|
#endif
|
|
#if defined(VARIABLE_CUSTOM4) && !defined(HAS_ATTRIBUTE_COLOR)
|
|
VARIABLE_CUSTOM_AT4 = material.VARIABLE_CUSTOM4;
|
|
#endif
|
|
|
|
// The world position can be changed by the user in materialVertex()
|
|
vertex_worldPosition.xyz = material.worldPosition.xyz;
|
|
|
|
#ifdef HAS_ATTRIBUTE_TANGENTS
|
|
vertex_worldNormal = material.worldNormal;
|
|
#endif
|
|
|
|
#if defined(VARIANT_HAS_SHADOWING) && defined(VARIANT_HAS_DIRECTIONAL_LIGHTING)
|
|
vertex_lightSpacePosition = computeLightSpacePosition(
|
|
vertex_worldPosition.xyz, vertex_worldNormal,
|
|
frameUniforms.lightDirection,
|
|
shadowUniforms.shadows[0].normalBias,
|
|
shadowUniforms.shadows[0].lightFromWorldMatrix);
|
|
#endif
|
|
|
|
#endif // !defined(USE_OPTIMIZED_DEPTH_VERTEX_SHADER)
|
|
|
|
vec4 position;
|
|
|
|
#if defined(VERTEX_DOMAIN_DEVICE)
|
|
// The other vertex domains are handled in initMaterialVertex()->computeWorldPosition()
|
|
position = getPosition();
|
|
|
|
#if !defined(USE_OPTIMIZED_DEPTH_VERTEX_SHADER)
|
|
#if defined(MATERIAL_HAS_CLIP_SPACE_TRANSFORM)
|
|
position = getMaterialClipSpaceTransform(material) * position;
|
|
#endif
|
|
#endif // !USE_OPTIMIZED_DEPTH_VERTEX_SHADER
|
|
|
|
#if defined(MATERIAL_HAS_VERTEX_DOMAIN_DEVICE_JITTERED)
|
|
// Apply the clip-space transform which is normally part of the projection
|
|
position.xy = position.xy * frameUniforms.clipTransform.xy + (position.w * frameUniforms.clipTransform.zw);
|
|
#endif
|
|
#else
|
|
position = getClipFromWorldMatrix() * getWorldPosition(material);
|
|
#endif
|
|
|
|
#if defined(VERTEX_DOMAIN_DEVICE)
|
|
// GL convention to inverted DX convention (must happen after clipSpaceTransform)
|
|
position.z = position.z * -0.5 + 0.5;
|
|
#endif
|
|
|
|
#if defined(VARIANT_HAS_VSM)
|
|
// For VSM, we use the linear light-space Z coordinate as the depth metric, which works for both
|
|
// directional and spot lights and can be safely interpolated.
|
|
// The value is guaranteed to be between [-znear, -zfar] by construction of viewFromWorldMatrix,
|
|
// (see ShadowMap.cpp).
|
|
// Use vertex_worldPosition.w which is otherwise not used to store the interpolated
|
|
// light-space depth.
|
|
highp float z = (getViewFromWorldMatrix() * getWorldPosition(material)).z;
|
|
|
|
// rescale [near, far] to [0, 1]
|
|
highp float depth = -z * frameUniforms.oneOverFarMinusNear - frameUniforms.nearOverFarMinusNear;
|
|
|
|
// remap depth between -1 and 1
|
|
depth = depth * 2.0 - 1.0;
|
|
|
|
vertex_worldPosition.w = depth;
|
|
#endif
|
|
|
|
// this must happen before we compensate for vulkan below
|
|
vertex_position = position;
|
|
|
|
#if defined(VARIANT_HAS_STEREO) && defined(FILAMENT_STEREO_INSTANCED)
|
|
// We're transforming a vertex whose x coordinate is within the range (-w to w).
|
|
// To move it to the correct portion of the viewport, we need to modify the x coordinate.
|
|
// It's important to do this after computing vertex_position.
|
|
int eyeIndex = instance_index % CONFIG_STEREO_EYE_COUNT;
|
|
|
|
float ndcViewportWidth = 2.0 / float(CONFIG_STEREO_EYE_COUNT); // the width of ndc space is 2
|
|
float eyeZeroMidpoint = -1.0f + ndcViewportWidth / 2.0;
|
|
|
|
float transform = eyeZeroMidpoint + ndcViewportWidth * float(eyeIndex);
|
|
position.x *= 1.0 / float(CONFIG_STEREO_EYE_COUNT);
|
|
position.x += transform * position.w;
|
|
|
|
// A fragment is clipped when gl_ClipDistance is negative (outside the clip plane).
|
|
|
|
float leftClip = position.x +
|
|
(1.0 - ndcViewportWidth * float(eyeIndex)) * position.w;
|
|
float rightClip = position.x +
|
|
(1.0 - ndcViewportWidth * float(eyeIndex + 1)) * position.w;
|
|
FILAMENT_CLIPDISTANCE[0] = leftClip;
|
|
FILAMENT_CLIPDISTANCE[1] = -rightClip;
|
|
#endif
|
|
|
|
#if defined(TARGET_VULKAN_ENVIRONMENT)
|
|
// In Vulkan, clip space is Y-down. In OpenGL and Metal, clip space is Y-up.
|
|
position.y = -position.y;
|
|
#endif
|
|
|
|
#if !defined(TARGET_VULKAN_ENVIRONMENT) && !defined(TARGET_METAL_ENVIRONMENT)
|
|
// This is not needed in Vulkan or Metal because clipControl is always (1, 0)
|
|
// (We don't use a dot() here because it workaround a spirv-opt optimization that in turn
|
|
// causes a crash on PowerVR, see #5118)
|
|
position.z = position.z * frameUniforms.clipControl.x + position.w * frameUniforms.clipControl.y;
|
|
#endif
|
|
|
|
// some PowerVR drivers crash when gl_Position is written more than once
|
|
gl_Position = position;
|
|
|
|
#if defined(VARIANT_HAS_STEREO) && defined(FILAMENT_STEREO_INSTANCED)
|
|
// Fragment shaders filter out the stereo variant, so we need to set instance_index here.
|
|
instance_index = logical_instance_index;
|
|
#endif
|
|
}
|