Skip to content

Commit

Permalink
Add .vs GLSL vertex shader extension (#6536)
Browse files Browse the repository at this point in the history
* Add missing GLSL vertex shader extension

* Add .vs file for #6536

Sample GLSL file with a `.vs` extension from Google's filament repo: https://github.com/google/filament/blob/main/shaders/src/main.vs
Licensed under Apache License 2.0

* Add another .vs file for #6536

From file from the https://github.com/FrostKiwi/Mirrorball/blob/main/src/shd/project.vs repo https://github.com/FrostKiwi/Mirrorball under the MIT licence.

* Update lib/linguist/languages.yml

---------

Co-authored-by: Colin Seymour <colin@github.com>
  • Loading branch information
FrostKiwi and lildude authored Sep 7, 2023
1 parent 55cfb49 commit 9b634a0
Show file tree
Hide file tree
Showing 3 changed files with 271 additions and 0 deletions.
1 change: 1 addition & 0 deletions lib/linguist/languages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2198,6 +2198,7 @@ GLSL:
- ".tese"
- ".vert"
- ".vrx"
- ".vs"
- ".vsh"
- ".vshader"
tm_scope: source.glsl
Expand Down
258 changes: 258 additions & 0 deletions samples/GLSL/main.vs
Original file line number Diff line number Diff line change
@@ -0,0 +1,258 @@
/*
* 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_INSTANCED_STEREO)
#if !defined(FILAMENT_HAS_FEATURE_INSTANCING)
#error Instanced stereo not supported at this feature level
#endif
// The lowest bit of the instance index represents the eye.
// This logic must be updated if CONFIG_STEREOSCOPIC_EYES changes
logical_instance_index = instance_index >> 1;
#endif

initObjectUniforms();

// Initialize the inputs to sensible default values, see 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) {
skinNormal(material.worldNormal, mesh_bone_indices, mesh_bone_weights);
skinNormal(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

// 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_INSTANCED_STEREO)
// This logic must be updated if CONFIG_STEREOSCOPIC_EYES changes
// We're transforming a vertex whose x coordinate is within the range (-w to w).
// To move it to the correct half of the viewport, we need to modify the x coordinate:
// Eye 0 (left half): (-w to 0)
// Eye 1 (right half): ( 0 to w)
// It's important to do this after computing vertex_position.
int eyeIndex = instance_index % 2;
float eyeShift = float(eyeIndex) * 2.0f - 1.0f; // eye 0: -1.0, eye 1: 1.0
position.x = position.x * 0.5f + (position.w * 0.5 * eyeShift);

// A fragment is clipped when gl_ClipDistance is negative (outside the clip plane). So,
// Eye 0 should have a positive value when x is < 0
// -position.x
// Eye 1 should have a positive value when x is > 0
// position.x
FILAMENT_CLIPDISTANCE[0] = position.x * eyeShift;
#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_INSTANCED_STEREO)
// Fragment shaders filter out the stereo variant, so we need to set instance_index here.
instance_index = logical_instance_index;
#endif
}
12 changes: 12 additions & 0 deletions samples/GLSL/project.vs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#version 100
attribute vec2 pos;
attribute vec3 rayvtx;
varying vec3 Ray;
uniform vec4 split;

void main()
{
Ray = rayvtx;
gl_Position = vec4(pos * vec2(split.z, split.w) + vec2(split.x, split.y),
0.0, 1.0);
}

0 comments on commit 9b634a0

Please sign in to comment.