Initial public source release

Split reusable rendering and format support into vectorail-core and vectorail-gc.
This commit is contained in:
2026-08-02 17:05:27 +02:00
commit 831d96e562
109 changed files with 20558 additions and 0 deletions
+39
View File
@@ -0,0 +1,39 @@
#version 450 core
out vec4 FragColor;
in vec2 vUv;
uniform float uTime;
uniform vec3 uResolution;
uniform sampler2D uBackdrop;
uniform bool uHasBackdrop;
uniform float uBackdropAspect;
uniform vec3 uBgTopRight;
uniform vec3 uBgTopLeft;
uniform vec3 uBgBottomRight;
uniform vec3 uBgBottomLeft;
void main() {
vec3 top = mix(uBgTopLeft, uBgTopRight, vUv.x);
vec3 bottom = mix(uBgBottomLeft, uBgBottomRight, vUv.x);
vec3 color = mix(bottom, top, vUv.y);
if (uHasBackdrop) {
// The useful jacket cell occupies the left square of GC's 512x256
// *_menu.dds atlas. Crop that cell and cover the portrait viewport.
float screenAspect = uResolution.x / uResolution.y;
vec2 imageUv = vUv - 0.5;
if (screenAspect < uBackdropAspect) {
imageUv.x *= screenAspect / uBackdropAspect;
} else {
imageUv.y *= uBackdropAspect / screenAspect;
}
imageUv += 0.5;
// Jacket cell bounds are 197x197 in the original 512x256 atlas.
vec2 atlasUv = vec2(imageUv.x * (197.0 / 512.0),
(1.0 - imageUv.y) * (197.0 / 256.0));
vec4 backdrop = texture(uBackdrop, atlasUv);
float vignette = 1.0 - smoothstep(0.18, 0.82, length(vUv - 0.5));
color = mix(color, backdrop.rgb * (0.08 + 0.05 * vignette), backdrop.a * 0.45);
}
FragColor = vec4(color, 1.0);
}
+9
View File
@@ -0,0 +1,9 @@
#version 450 core
layout (location = 0) in vec2 aPos;
out vec2 vUv;
uniform vec3 uGameplayFlip;
void main() {
vUv = aPos * 0.5 + 0.5;
gl_Position = vec4(aPos * uGameplayFlip.xy, 0.0, 1.0);
}
+39
View File
@@ -0,0 +1,39 @@
#version 450 core
in float vSide;
in float vDist;
in float vTrackTimeMs;
out vec4 FragColor;
uniform vec3 uColor;
uniform vec3 uBehindColor;
uniform bool uIsAvatar;
uniform bool uClipTrack;
uniform float uCurrentTimeMs;
uniform float uDrawBehindMs;
uniform float uDrawAheadMs;
uniform float uAlpha;
void main() {
float dist = abs(vSide);
// Мягкий туман: трасса видна очень далеко
float fog = exp(-vDist * 0.001);
if (uClipTrack &&
(vTrackTimeMs < uCurrentTimeMs - uDrawBehindMs ||
vTrackTimeMs > uCurrentTimeMs + uDrawAheadMs)) {
discard;
}
if (uIsAvatar) {
FragColor = vec4(1.0, 1.0, 1.0, 1.0);
} else {
float intensity = pow(1.0 - dist, 4.0);
float core = pow(1.0 - dist, 20.0);
vec3 railColor = (uClipTrack && vTrackTimeMs < uCurrentTimeMs) ? uBehindColor : uColor;
vec3 finalColor = mix(railColor, vec3(1.0), core);
// Цвет затухает в темноту, а не просто обрезается
FragColor = vec4(finalColor * fog, intensity * fog * uAlpha);
}
}
+25
View File
@@ -0,0 +1,25 @@
#version 450 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in float aSide;
layout (location = 2) in vec3 aRight;
layout (location = 3) in float aTimeMs;
uniform mat4 uProjection;
uniform mat4 uView;
uniform float uWidth;
out float vSide;
out float vDist;
out float vTrackTimeMs;
void main() {
vSide = aSide;
vTrackTimeMs = aTimeMs;
vec3 finalPos = aPos + aRight * aSide * uWidth;
// Считаем позицию в пространстве камеры
vec4 viewPos = uView * vec4(finalPos, 1.0);
vDist = -viewPos.z; // Глубина (дистанция от камеры)
gl_Position = uProjection * viewPos;
}
+8
View File
@@ -0,0 +1,8 @@
#version 450 core
out vec4 FragColor;
uniform vec4 uColor;
void main() {
FragColor = uColor;
}
+10
View File
@@ -0,0 +1,10 @@
#version 450 core
layout (location = 0) in vec3 aPos;
uniform mat4 uProjection;
uniform mat4 uView;
uniform mat4 uModel;
void main() {
gl_Position = uProjection * uView * uModel * vec4(aPos, 1.0);
}
+12
View File
@@ -0,0 +1,12 @@
#version 450 core
in vec2 vUv;
out vec4 FragColor;
uniform sampler2D uTexture;
uniform vec4 uColor;
void main() {
vec4 texel = texture(uTexture, vUv);
if (texel.a < 0.01) discard;
FragColor = vec4(texel.rgb * uColor.rgb, texel.a * uColor.a);
}
+14
View File
@@ -0,0 +1,14 @@
#version 450 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec2 aUv;
uniform mat4 uProjection;
uniform mat4 uView;
uniform vec4 uUvRect;
out vec2 vUv;
void main() {
vUv = uUvRect.xy + aUv * uUvRect.zw;
gl_Position = uProjection * uView * vec4(aPos, 1.0);
}
+9
View File
@@ -0,0 +1,9 @@
#version 450 core
in float vLife;
out vec4 FragColor;
uniform vec3 uColor;
void main() {
// В кваде vLife достаточно для затухания
FragColor = vec4(uColor, vLife);
}
+17
View File
@@ -0,0 +1,17 @@
#version 450 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec2 aOffset; // Смещение углов квадрата
layout (location = 2) in float aLife;
uniform mat4 uProjection;
uniform mat4 uView;
out float vLife;
void main() {
vLife = aLife;
vec4 viewPos = uView * vec4(aPos, 1.0);
// Billboard: квадрат всегда смотрит на камеру
viewPos.xy += aOffset * aLife * 0.5;
gl_Position = uProjection * viewPos;
}
+7
View File
@@ -0,0 +1,7 @@
#version 450 core
in vec4 vColor;
out vec4 FragColor;
void main() {
FragColor = vColor;
}
+13
View File
@@ -0,0 +1,13 @@
#version 450 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec4 aColor;
uniform mat4 uProjection;
uniform mat4 uView;
out vec4 vColor;
void main() {
vColor = aColor;
gl_Position = uProjection * uView * vec4(aPos, 1.0);
}
+19
View File
@@ -0,0 +1,19 @@
#version 450 core
in vec2 vUv;
out vec4 FragColor;
uniform sampler2D uTexture;
uniform bool uUseTexture;
uniform bool uUseGradient;
uniform vec4 uColor;
uniform vec4 uGradientTop;
uniform vec4 uGradientBottom;
void main() {
if (uUseTexture) {
FragColor = texture(uTexture, vUv) * uColor;
} else if (uUseGradient) {
FragColor = mix(uGradientTop, uGradientBottom, vUv.y);
} else {
FragColor = uColor;
}
}
+9
View File
@@ -0,0 +1,9 @@
#version 450 core
layout (location = 0) in vec2 aPos;
layout (location = 1) in vec2 aUv;
out vec2 vUv;
void main() {
vUv = aUv;
gl_Position = vec4(aPos, 0.0, 1.0);
}