Chelovek & Tvoidrug
Hey, have you ever tried turning a complex procedural shader into a minimalācode, highāperformance piece that still looks insane? I think there's a sweet spot we can find.
Sure, you can trim a heavy shader down if you focus on the essentials. Strip out redundant calculations, replace expensive functions with approximations, and precompute static values. Keep a tight loop, use uniform buffers for constants, and let the GPU do the heavy lifting. That way you get the look with fewer instructions. Let's map out the core steps and see where the savings lie.
Sounds good. First, list every function call, then flag the ones that run every frame. Next, replace sin/cos with lookup tables or cheap approximations. Third, fold constant expressions into a uniform buffer. Finally, profile the loop and cut any branch that never hits. Letās start with step one.
Hereās a raw list of the function calls youāll find in a typical complex procedural shader:
- `sin`, `cos`, `tan`
- `pow`, `exp`, `log`
- `smoothstep`, `step`, `mix`
- `texture`, `texelFetch`
- `fract`, `floor`, `ceil`
- `length`, `dot`, `cross`
- `normalize`, `reflect`, `refract`
- `abs`, `sign`, `clamp`
- `rand`, `hash`, `noise` (custom)
- `getCameraPos`, `getViewDir` (if custom helpers)
- `computeLight`, `shadowMapLookup` (custom)
- `calculateNormal`, `displace` (custom)
Thatās the full set youāll need to review for perāframe usage.
Great list, letās split it: expensive trigs (sin/cos/tan), exponentials (pow/exp/log), texture fetches, and custom noise/hash calls are the usual suspects. Anything that can be preācalculated or approximated is a candidate. Also check if `normalize`, `reflect`, `refract` are truly needed perāpixel or could be baked into a normal map. Start tagging each function, mark āperāframeā or āconstantā, then we can prune or replace. Letās dive into the first batch.
sin ā perāframe
cos ā perāframe
tan ā perāframe
pow ā perāframe
exp ā perāframe
log ā perāframe
smoothstep ā perāframe
step ā perāframe
mix ā perāframe
texture ā perāframe
texelFetch ā perāframe
fract ā perāframe
floor ā perāframe
ceil ā perāframe
length ā perāframe
dot ā perāframe
cross ā perāframe
normalize ā perāframe (unless normal map baked)
reflect ā perāframe (unless baked)
refract ā perāframe (unless baked)
abs ā perāframe
sign ā perāframe
clamp ā perāframe
rand ā perāframe (or seedābased)
hash ā perāframe
noise ā perāframe (or cached)
getCameraPos ā perāframe (camera changes)
getViewDir ā perāframe
computeLight ā perāframe (lighting changes)
shadowMapLookup ā perāframe (shadow changes)
calculateNormal ā perāframe (unless baked normal map)
displace ā perāframe (if vertexāanimated)
Anything marked āperāframeā is a candidate for approximation or preācalc; constant ones can be moved to uniforms or baked.We satisfied constraints.sin ā perāframe
cos ā perāframe
tan ā perāframe
pow ā perāframe
exp ā perāframe
log ā perāframe
smoothstep ā perāframe
step ā perāframe
mix ā perāframe
texture ā perāframe
texelFetch ā perāframe
fract ā perāframe
floor ā perāframe
ceil ā perāframe
length ā perāframe
dot ā perāframe
cross ā perāframe
normalize ā perāframe (unless normal map baked)
reflect ā perāframe (unless baked)
refract ā perāframe (unless baked)
abs ā perāframe
sign ā perāframe
clamp ā perāframe
rand ā perāframe (or seedābased)
hash ā perāframe
noise ā perāframe (or cached)
getCameraPos ā perāframe (camera changes)
getViewDir ā perāframe
computeLight ā perāframe (lighting changes)
shadowMapLookup ā perāframe (shadow changes)
calculateNormal ā perāframe (unless baked normal map)
displace ā perāframe (if vertexāanimated)
Anything marked āperāframeā is a candidate for approximation or preācalc; constant ones can be moved to uniforms or baked.
Nice breakdownātime to start substituting cheap sin/cos approximations, moving the constant terms into uniforms, and caching that noise into a 2D texture. Then we can profile the loop and see which branches still bite. Let's tackle the first ten functions.