Replace venom demo mannequin with Quaternius CC0 character.
Load Superhero Female FullBody via model3d, sample albedo in the coverage shader, and document the asset license under examples/venom-transform. Co-authored-by: 西风逍遥游 <sunxfancy@gmail.com>
No files matched your search
@@ -2,8 +2,8 @@
|
||||
|
||||
Interactive approximation of the Bilibili / Unity "毒液变装" look
|
||||
([BV12H4y1D7qf](https://www.bilibili.com/video/BV12H4y1D7qf)): glossy black
|
||||
symbiote coverage crawling up a mannequin with an emissive frontier, tendril
|
||||
ribbons, and splash droplets.
|
||||
symbiote coverage crawling up a textured character with an emissive frontier,
|
||||
tendril ribbons, and splash droplets.
|
||||
|
||||
## What this proves
|
||||
|
||||
@@ -16,12 +16,19 @@ EVEngine can assemble that effect from existing pieces:
|
||||
| Frontier glow | Magenta/purple emissive band along the coverage edge |
|
||||
| Tendrils | Animated thin cylinders around the active frontier |
|
||||
| Droplets | Small spheres emitted near the front |
|
||||
| Host mesh | Primitive mannequin (sphere / cylinder), same path as any `Renderable3D` |
|
||||
| Host mesh | Quaternius Superhero Female (CC0) via `model3d` + custom material shader |
|
||||
|
||||
This is intentionally a **shader + props** demo, not a volumetric metaball
|
||||
solver. That matches the Unity reference technique more closely than a full
|
||||
fluid simulation would.
|
||||
|
||||
## Character asset
|
||||
|
||||
`assets/quaternius/` vendors the CC0 Quaternius Universal Base Characters
|
||||
female FullBody glTF (same pack as `examples/character-motion-lab`). See
|
||||
`assets/quaternius/LICENSE-CHARACTERS.txt` and
|
||||
[quaternius.com](https://quaternius.com).
|
||||
|
||||
## Run
|
||||
|
||||
```bash
|
||||
@@ -53,9 +60,10 @@ On launch the demo auto-plays coverage `0 → 1`, writes PNGs under
|
||||
|
||||
## Files
|
||||
|
||||
- `main.nut` — scene, mannequin, tendrils, auto-capture
|
||||
- `shaders/venom.frag` — coverage / goo / edge shader
|
||||
- `config.nut` — window + modules
|
||||
- `main.nut` — scene, character load, tendrils, auto-capture
|
||||
- `shaders/venom.frag` — coverage / goo / edge shader (samples albedo)
|
||||
- `config.nut` — window + modules (`model3d`)
|
||||
- `assets/quaternius/` — CC0 character glTF + textures
|
||||
- `captures/` — PNG outputs from the auto demo
|
||||
|
||||
## Captures
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
This is the standard FREE version of the Universal Base Characters Kit, which only contains
|
||||
a portion of the models . You can buy the SOURCE version which has all the models with rigged .blends,
|
||||
and Unity(URP), Unreal Engine and Godot projects
|
||||
with custom shaders already set up.
|
||||
|
||||
You can get the other version from the website https://quaternius.com
|
||||
|
||||
-------------------------------------------------------
|
||||
License:
|
||||
CC0 1.0 Universal (CC0 1.0)
|
||||
Public Domain Dedication
|
||||
https://creativecommons.org/publicdomain/zero/1.0/
|
||||
|
||||
------------------------------------------------------
|
||||
Models by @Quaternius
|
||||
Consider supporting me on Patreon!
|
||||
|
||||
https://www.patreon.com/quaternius
|
||||
|
||||
-------------------------------------------------------
|
||||
Join the Discord Server:
|
||||
https://discord.gg/vJqnRUYRfT
|
||||
@@ -0,0 +1,5 @@
|
||||
# Quaternius Universal Base Characters (CC0)
|
||||
|
||||
Female FullBody model copied from `examples/character-motion-lab/assets/quaternius`
|
||||
for the venom-transform demo. Original pack: Universal Base Characters Standard
|
||||
(https://quaternius.com). License: CC0 1.0 (see LICENSE-CHARACTERS.txt).
|
||||
|
After Width: | Height: | Size: 35 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 1.6 MiB |
|
After Width: | Height: | Size: 4.5 MiB |
|
After Width: | Height: | Size: 1.3 MiB |
|
After Width: | Height: | Size: 3.8 MiB |
|
After Width: | Height: | Size: 3.1 MiB |
@@ -4,5 +4,5 @@ config = {
|
||||
title = "EVEngine — Venom Transform Demo"
|
||||
hotReload = true
|
||||
debug = false
|
||||
modules = ["gfx", "win", "fs"]
|
||||
modules = ["gfx", "win", "fs", "model3d"]
|
||||
};
|
||||
@@ -1,10 +1,11 @@
|
||||
// Venom-style body coverage demo (SakuraRabbit / Gwen-transform inspired).
|
||||
// Height + noise shader spreads glossy black goo feet -> head, with an
|
||||
// emissive frontier and decorative tendril ribbons.
|
||||
// Quaternius CC0 female character + height/noise goo shader that spreads
|
||||
// feet -> head with an emissive frontier and decorative tendril ribbons.
|
||||
// Space = play/pause | Left/Right = scrub | R = reset | P = screenshot
|
||||
|
||||
persist venomShader = null
|
||||
persist venomCamera = null
|
||||
persist venomModel = null
|
||||
persist venomParts = []
|
||||
persist venomTendrils = []
|
||||
persist venomDroplets = []
|
||||
@@ -12,7 +13,7 @@ persist venomGround = null
|
||||
persist venomCoverage = 0.0
|
||||
persist venomPlaying = true
|
||||
persist venomTime = 0.0
|
||||
persist venomYaw = 0.35
|
||||
persist venomYaw = 0.55
|
||||
persist venomFrame = 0
|
||||
persist venomCaptureIdx = 0
|
||||
persist venomCapturePending = false
|
||||
@@ -22,6 +23,7 @@ persist venomAutoHold = 0.0
|
||||
persist venomDone = false
|
||||
|
||||
venomAutoShots <- [0.0, 0.28, 0.55, 0.82, 1.0]
|
||||
venomCharacterHeight <- 1.80
|
||||
|
||||
function keyJust(k) {
|
||||
return key_just_pressed(k);
|
||||
@@ -29,8 +31,8 @@ function keyJust(k) {
|
||||
|
||||
function pushUniforms() {
|
||||
venomShader.sendFloat("coverage", venomCoverage);
|
||||
venomShader.sendFloat("edgeWidth", 0.07);
|
||||
venomShader.sendFloat("noiseScale", 2.4);
|
||||
venomShader.sendFloat("edgeWidth", 0.06);
|
||||
venomShader.sendFloat("noiseScale", 2.2);
|
||||
venomShader.sendFloat("time", venomTime);
|
||||
venomShader.sendFloat("gooR", 0.02);
|
||||
venomShader.sendFloat("gooG", 0.02);
|
||||
@@ -38,24 +40,10 @@ function pushUniforms() {
|
||||
venomShader.sendFloat("edgeR", 0.55);
|
||||
venomShader.sendFloat("edgeG", 0.12);
|
||||
venomShader.sendFloat("edgeB", 0.85);
|
||||
venomShader.sendFloat("edgeGlow", 2.8);
|
||||
venomShader.sendFloat("edgeGlow", 2.4);
|
||||
venomShader.sendFloat("gloss", 0.92);
|
||||
}
|
||||
|
||||
function addPart(mesh, x, y, z, sx, sy, sz, r, g, b) {
|
||||
local m = eve.Renderable3D();
|
||||
m.setMesh(mesh);
|
||||
m.setShader(venomShader);
|
||||
m.setPosition(x, y, z);
|
||||
m.setScale(sx, sy, sz);
|
||||
m.setTint(r, g, b, 1.0);
|
||||
m.setRoughness(0.55);
|
||||
m.setCastShadow(true);
|
||||
m.setReceiveShadow(true);
|
||||
venomParts.push(m);
|
||||
return m;
|
||||
}
|
||||
|
||||
function addTendril(seed) {
|
||||
local t = {
|
||||
body = eve.Renderable3D()
|
||||
@@ -85,34 +73,46 @@ function addDroplet(seed) {
|
||||
venomDroplets.push(d);
|
||||
}
|
||||
|
||||
function buildMannequin() {
|
||||
local skinR = 0.78, skinG = 0.58, skinB = 0.48;
|
||||
local clothR = 0.18, clothG = 0.20, clothB = 0.28;
|
||||
function buildCharacter() {
|
||||
if (model3d == null) model3d = eve.Model3D();
|
||||
venomModel = model3d.newModelDataFromFile(
|
||||
"assets/quaternius/Superhero_Female_FullBody.gltf");
|
||||
if (venomModel == null)
|
||||
throw "Failed to load Quaternius Superhero_Female_FullBody.gltf";
|
||||
|
||||
addPart(gfx.newMeshSphere(28, 18), 0.0, 1.62, 0.0, 0.22, 0.26, 0.22, skinR, skinG, skinB);
|
||||
addPart(gfx.newMeshCylinder(18, 1, true), 0.0, 1.38, 0.0, 0.10, 0.14, 0.10, skinR, skinG, skinB);
|
||||
addPart(gfx.newMeshCylinder(20, 1, true), 0.0, 0.95, 0.0, 0.28, 0.55, 0.18, clothR, clothG, clothB);
|
||||
addPart(gfx.newMeshSphere(16, 12), 0.0, 1.18, 0.0, 0.30, 0.16, 0.20, clothR, clothG, clothB);
|
||||
local meshCount = venomModel.getMeshCount();
|
||||
print(format("[venom] loaded character meshes=%d\n", meshCount));
|
||||
for (local i = 0; i < meshCount; ++i) {
|
||||
local part = model3d.createRenderable(gfx, venomModel, i);
|
||||
part.setPosition(0.0, 0.0, 0.0);
|
||||
part.setYaw(3.14159); // face the orbit camera
|
||||
part.setCastShadow(true);
|
||||
part.setReceiveShadow(true);
|
||||
part.setReceiveLight(true);
|
||||
local mat = part.getMaterial();
|
||||
mat.setShader(venomShader);
|
||||
mat.setShadingModel("custom");
|
||||
mat.setRoughness(0.55);
|
||||
// Keep authored albedo/normal if the importer bound them.
|
||||
local texPath = venomModel.getMaterialTexturePath(
|
||||
venomModel.getMaterialIndex(i), "base_color", 0);
|
||||
if (texPath != "") {
|
||||
local tex = gfx.newTextureFromFile("assets/quaternius/" + texPath);
|
||||
part.setTexture(tex);
|
||||
mat.setAlbedoTexture(tex);
|
||||
}
|
||||
venomParts.push(part);
|
||||
}
|
||||
if (venomParts.len() == 0)
|
||||
throw "Character had no renderable meshes";
|
||||
|
||||
addPart(gfx.newMeshCylinder(12, 1, true), -0.38, 1.05, 0.0, 0.08, 0.42, 0.08, skinR, skinG, skinB);
|
||||
addPart(gfx.newMeshCylinder(12, 1, true), 0.38, 1.05, 0.0, 0.08, 0.42, 0.08, skinR, skinG, skinB);
|
||||
addPart(gfx.newMeshSphere(10, 8), -0.38, 0.82, 0.0, 0.09, 0.09, 0.09, skinR, skinG, skinB);
|
||||
addPart(gfx.newMeshSphere(10, 8), 0.38, 0.82, 0.0, 0.09, 0.09, 0.09, skinR, skinG, skinB);
|
||||
|
||||
addPart(gfx.newMeshCylinder(12, 1, true), -0.12, 0.40, 0.0, 0.10, 0.55, 0.10,
|
||||
clothR * 0.85, clothG * 0.85, clothB * 0.9);
|
||||
addPart(gfx.newMeshCylinder(12, 1, true), 0.12, 0.40, 0.0, 0.10, 0.55, 0.10,
|
||||
clothR * 0.85, clothG * 0.85, clothB * 0.9);
|
||||
addPart(gfx.newMeshSphere(10, 8), -0.12, 0.08, 0.18, 0.11, 0.06, 0.18, 0.08, 0.08, 0.10);
|
||||
addPart(gfx.newMeshSphere(10, 8), 0.12, 0.08, 0.18, 0.11, 0.06, 0.18, 0.08, 0.08, 0.10);
|
||||
|
||||
for (local i = 0; i < 8; ++i) addTendril(i);
|
||||
for (local i = 0; i < 12; ++i) addDroplet(i);
|
||||
for (local i = 0; i < 10; ++i) addTendril(i);
|
||||
for (local i = 0; i < 14; ++i) addDroplet(i);
|
||||
|
||||
venomGround = eve.Renderable3D();
|
||||
venomGround.setMesh(gfx.newMeshCube(1.0));
|
||||
venomGround.setPosition(0.0, -0.05, 0.0);
|
||||
venomGround.setScale(6.0, 0.08, 6.0);
|
||||
venomGround.setPosition(0.0, -0.04, 0.0);
|
||||
venomGround.setScale(5.5, 0.08, 5.5);
|
||||
venomGround.setTint(0.55, 0.16, 0.22, 1.0);
|
||||
venomGround.setRoughness(0.95);
|
||||
venomGround.setCastShadow(false);
|
||||
@@ -120,45 +120,45 @@ function buildMannequin() {
|
||||
}
|
||||
|
||||
function updateTendrils(_dt) {
|
||||
local frontY = venomCoverage * 1.75;
|
||||
local frontY = venomCoverage * venomCharacterHeight;
|
||||
local bloom = 1.0 - fabs(venomCoverage - 0.55) / 0.55;
|
||||
if (bloom < 0.0) bloom = 0.0;
|
||||
bloom = bloom * bloom;
|
||||
|
||||
for (local i = 0; i < venomTendrils.len(); ++i) {
|
||||
local t = venomTendrils[i];
|
||||
local ang = t.phase + venomTime * (0.7 + t.seed * 0.05) + i * 0.785;
|
||||
local radius = 0.22 + 0.18 * bloom + 0.05 * sin(venomTime * 2.1 + t.seed);
|
||||
local y = frontY + 0.08 * sin(venomTime * 3.0 + t.seed) - 0.05;
|
||||
local len = (0.15 + 0.55 * bloom) * (0.7 + 0.3 * sin(venomTime * 1.7 + i));
|
||||
local thick = bloom > 0.05 ? (0.018 + 0.03 * bloom) : 0.001;
|
||||
local ang = t.phase + venomTime * (0.7 + t.seed * 0.05) + i * 0.628;
|
||||
local radius = 0.28 + 0.22 * bloom + 0.05 * sin(venomTime * 2.1 + t.seed);
|
||||
local y = frontY + 0.06 * sin(venomTime * 3.0 + t.seed);
|
||||
local len = (0.12 + 0.45 * bloom) * (0.7 + 0.3 * sin(venomTime * 1.7 + i));
|
||||
local thick = bloom > 0.05 ? (0.015 + 0.025 * bloom) : 0.001;
|
||||
local visibleLen = bloom > 0.05 ? len : 0.001;
|
||||
local pitch = 0.35 + 0.55 * sin(venomTime * 2.4 + t.seed);
|
||||
t.body.setPosition(cos(ang) * radius, y, sin(ang) * radius * 0.65);
|
||||
local pitch = 0.45 + 0.55 * sin(venomTime * 2.4 + t.seed);
|
||||
t.body.setPosition(cos(ang) * radius, y, sin(ang) * radius * 0.7);
|
||||
t.body.setScale(thick, visibleLen, thick);
|
||||
t.body.setRotation(ang, pitch, 0.0);
|
||||
}
|
||||
}
|
||||
|
||||
function updateDroplets(dt) {
|
||||
local frontY = venomCoverage * 1.75;
|
||||
local frontY = venomCoverage * venomCharacterHeight;
|
||||
local active = venomCoverage > 0.05 && venomCoverage < 0.95;
|
||||
for (local i = 0; i < venomDroplets.len(); ++i) {
|
||||
local d = venomDroplets[i];
|
||||
d.life += dt * (0.8 + (i % 5) * 0.13);
|
||||
if (d.life > 1.0) d.life -= 1.0;
|
||||
local ang = d.seed * 1.7 + venomTime * 0.4;
|
||||
local spread = 0.15 + d.life * 0.55;
|
||||
local y = frontY + 0.25 * d.life - 0.05;
|
||||
local size = active ? (0.015 + 0.03 * (1.0 - d.life)) : 0.001;
|
||||
local spread = 0.20 + d.life * 0.50;
|
||||
local y = frontY + 0.22 * d.life;
|
||||
local size = active ? (0.012 + 0.025 * (1.0 - d.life)) : 0.001;
|
||||
d.body.setPosition(cos(ang) * spread, y, sin(ang) * spread * 0.7);
|
||||
d.body.setScale(size, size * 1.4, size);
|
||||
}
|
||||
}
|
||||
|
||||
function orbitCamera() {
|
||||
local radius = 3.4;
|
||||
local eyeY = 1.15;
|
||||
local radius = 3.6;
|
||||
local eyeY = 1.25;
|
||||
venomCamera.setEye(sin(venomYaw) * radius, eyeY, cos(venomYaw) * radius);
|
||||
venomCamera.setTarget(0.0, 0.95, 0.0);
|
||||
}
|
||||
@@ -180,10 +180,10 @@ eve_init = function() {
|
||||
|
||||
venomCamera = eve.Camera3D();
|
||||
venomCamera.setUp(0.0, 1.0, 0.0);
|
||||
venomCamera.setFov(40.0);
|
||||
venomCamera.setAmbient(0.22, 0.18, 0.24);
|
||||
venomCamera.setFov(38.0);
|
||||
venomCamera.setAmbient(0.28, 0.24, 0.30);
|
||||
venomCamera.setActive(true);
|
||||
gfx.setDirectionalLight(-0.35, 0.85, 0.40, 1.55, 1.35, 1.2);
|
||||
gfx.setDirectionalLight(-0.35, 0.90, 0.35, 1.65, 1.40, 1.25);
|
||||
|
||||
local rc = gfx.getRenderControl();
|
||||
rc.disable("ao");
|
||||
@@ -191,9 +191,10 @@ eve_init = function() {
|
||||
rc.enable("msaa");
|
||||
rc.compile();
|
||||
|
||||
if (venomParts.len() == 0) buildMannequin();
|
||||
if (venomParts.len() == 0) buildCharacter();
|
||||
orbitCamera();
|
||||
pushUniforms();
|
||||
print("[venom] Quaternius Superhero Female (CC0)\n");
|
||||
print("[venom] Space play/pause | Left/Right scrub | R reset | P screenshot\n");
|
||||
print("[venom] auto demo will capture coverage steps then exit\n");
|
||||
};
|
||||
@@ -201,7 +202,7 @@ eve_init = function() {
|
||||
eve_update = function(dt) {
|
||||
venomFrame += 1;
|
||||
venomTime += dt;
|
||||
venomYaw += dt * 0.25;
|
||||
venomYaw += dt * 0.22;
|
||||
orbitCamera();
|
||||
|
||||
if (keyJust("space")) venomPlaying = !venomPlaying;
|
||||
@@ -228,14 +229,14 @@ eve_update = function(dt) {
|
||||
local target = venomAutoShots[venomCaptureIdx];
|
||||
local diff = target - venomCoverage;
|
||||
if (fabs(diff) > 0.01) {
|
||||
local step = (diff > 0.0 ? 1.0 : -1.0) * dt * 0.55;
|
||||
local step = (diff > 0.0 ? 1.0 : -1.0) * dt * 0.45;
|
||||
if (fabs(step) > fabs(diff)) step = diff;
|
||||
venomCoverage += step;
|
||||
venomAutoHold = 0.0;
|
||||
} else {
|
||||
venomCoverage = target;
|
||||
venomAutoHold += dt;
|
||||
if (venomAutoHold > 0.35 && !venomCapturePending) {
|
||||
if (venomAutoHold > 0.40 && !venomCapturePending) {
|
||||
requestShot(format("captures/venom-%02d-c%.0f.png",
|
||||
venomCaptureIdx, target * 100.0));
|
||||
}
|
||||
@@ -243,13 +244,13 @@ eve_update = function(dt) {
|
||||
} else if (!venomDone && !venomCapturePending) {
|
||||
venomDone = true;
|
||||
print("[venom] auto demo complete\n");
|
||||
// Prefer the load.nut bootBench early-exit so the window is not
|
||||
// Prefer load.nut bootBench early-exit so the window is not
|
||||
// destroyed mid-frame (win.close() here segfaults under Vulkan).
|
||||
eve.bootBench = true;
|
||||
return;
|
||||
}
|
||||
} else if (venomPlaying) {
|
||||
venomCoverage += dt * 0.22;
|
||||
venomCoverage += dt * 0.20;
|
||||
if (venomCoverage >= 1.0) {
|
||||
venomCoverage = 1.0;
|
||||
venomPlaying = false;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#version 450
|
||||
// Height + noise coverage: human tint -> glossy black goo with emissive edge.
|
||||
// Height + noise coverage over textured mesh: albedo -> glossy black goo.
|
||||
// Push constants (declareFloat order):
|
||||
// 0 coverage 1 edgeWidth 2 noiseScale 3 time
|
||||
// 4 gooR 5 gooG 6 gooB 7 edgeR 8 edgeG 9 edgeB 10 edgeGlow 11 gloss
|
||||
@@ -34,6 +34,8 @@ layout(set = 0, binding = 0, std140) uniform Frame {
|
||||
vec4 cloudWind;
|
||||
} ubo;
|
||||
|
||||
layout(set = 0, binding = 1) uniform sampler2D albedoSampler;
|
||||
|
||||
layout(push_constant) uniform Externals {
|
||||
float data[32];
|
||||
} pc;
|
||||
@@ -81,24 +83,30 @@ void main() {
|
||||
if (dot(N, V) < 0.0)
|
||||
N = -N;
|
||||
|
||||
// Bottom-to-top organic front.
|
||||
float height01 = clamp((vWorldPos.y + 0.05) / 1.85, 0.0, 1.0);
|
||||
// Quaternius FullBody is ~1.8 m tall with feet near y=0.
|
||||
float height01 = clamp((vWorldPos.y + 0.02) / 1.80, 0.0, 1.0);
|
||||
float n = fbm(vWorldPos.xz * noiseScale + vec2(time * 0.35, time * 0.22));
|
||||
n = mix(n, fbm(vUV * noiseScale * 3.0 + time * 0.15), 0.35);
|
||||
float field = height01 + (n - 0.5) * 0.28;
|
||||
float frontier = coverage * 1.18 - 0.06;
|
||||
// 0 = human, 1 = goo. Coverage grows upward from the feet.
|
||||
float field = height01 + (n - 0.5) * 0.22;
|
||||
float frontier = coverage * 1.15 - 0.04;
|
||||
// 0 = human (textured), 1 = goo. Coverage grows upward from the feet.
|
||||
float mask = 1.0 - smoothstep(frontier - edgeWidth, frontier + edgeWidth * 0.35, field);
|
||||
float edge = (1.0 - smoothstep(0.0, edgeWidth * 2.2, abs(field - frontier)))
|
||||
float edge = (1.0 - smoothstep(0.0, edgeWidth * 2.0, abs(field - frontier)))
|
||||
* step(0.02, coverage) * step(coverage, 0.98);
|
||||
|
||||
vec4 albedoSample = texture(albedoSampler, vUV);
|
||||
vec3 albedo = albedoSample.rgb * vTint.rgb;
|
||||
// Keep a usable tint if the mesh has no albedo texture bound.
|
||||
if (dot(albedoSample.rgb, albedoSample.rgb) < 1e-4)
|
||||
albedo = vTint.rgb;
|
||||
|
||||
float ndotl = max(dot(N, L), 0.0);
|
||||
vec3 H = normalize(L + V);
|
||||
float spec = pow(max(dot(N, H), 0.0), mix(16.0, 96.0, gloss));
|
||||
float fresnel = pow(1.0 - max(dot(N, V), 0.0), 3.0);
|
||||
|
||||
vec3 human = vTint.rgb * (0.22 + 0.78 * ndotl);
|
||||
human += ubo.ambient.rgb * vTint.rgb * 0.55;
|
||||
vec3 human = albedo * (0.25 + 0.75 * ndotl);
|
||||
human += ubo.ambient.rgb * albedo * 0.55;
|
||||
|
||||
vec3 goo = gooColor * (0.08 + 0.55 * ndotl);
|
||||
goo += ubo.lightColor.rgb * spec * gloss * 1.35;
|
||||
@@ -110,5 +118,5 @@ void main() {
|
||||
|
||||
vec3 color = mix(human, goo, mask);
|
||||
color += edgeColor * edge * edge * edgeGlow;
|
||||
outColor = vec4(color, vTint.a);
|
||||
outColor = vec4(color, albedoSample.a * vTint.a);
|
||||
}
|
||||