Skip to content

Commit c91bd04

Browse files
committed
Feature: Cross products with two constant 3D vectors are simplified.
e.g. "vec3 v = cross(vec3(1, 2, 3), vec3(4, 5, 6));" => "vec3 v = vec3(-3, 6, -3);"
1 parent 599f79c commit c91bd04

File tree

5 files changed

+81
-31
lines changed

5 files changed

+81
-31
lines changed

ShaderShrinker/Shrinker.Parser/Optimizations/PerformArithmeticExtension.cs

Lines changed: 47 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -178,20 +178,7 @@ public static bool PerformArithmetic(this SyntaxNode rootNode)
178178
continue;
179179

180180
var mag = Math.Sqrt(nums.Sum(o => o * o));
181-
182-
// Squash vecN(n, n, n, ...) down to vecN(n)
183-
if (nums.Count > 1 && nums.Distinct().Count() == 1)
184-
nums = new List<double> { nums.First() };
185-
186-
var newVectorNumNodes = nums.SelectMany(o => new[]
187-
{
188-
new GenericSyntaxNode(new CommaToken()),
189-
new GenericSyntaxNode(FloatToken.From(o / mag, MaxDp).AsIntIfPossible())
190-
}).Skip(1);
191-
192-
var newVectorBrackets = new RoundBracketSyntaxNode(newVectorNumNodes);
193-
var newVectorNode = new GenericSyntaxNode(normalizeNode.Params.Children.First().Token.Content);
194-
normalizeNode.ReplaceWith(newVectorNode).InsertNextSibling(newVectorBrackets);
181+
ReplaceNodeWithVector(normalizeNode, nums.Select(o => o / mag).ToList());
195182
didChange = true;
196183
}
197184

@@ -234,6 +221,33 @@ public static bool PerformArithmetic(this SyntaxNode rootNode)
234221
didChange = true;
235222
}
236223

224+
// cross(vecN, vecN) => vecN(...)
225+
foreach (var crossNode in functionCalls
226+
.Where(o => o.Name == "cross" && o.Params.Children.Count(n => (n.Token as TypeToken)?.IsVector() == true) == 2)
227+
.ToList())
228+
{
229+
var crossParams = crossNode.Params.GetCsv().ToList();
230+
var vectors = crossParams.Select(o => o.First()).Where(o => (o.Token as TypeToken)?.IsVector() == true).ToList();
231+
var nums = vectors.Select(GetVectorNumericCsv).ToList();
232+
233+
if (nums.Count(o => o != null) != 2)
234+
continue; // Not a cross product of two 'simple' numeric vectors.
235+
236+
if (nums.Any(o => o.Count != 3))
237+
continue; // Not a pair of 3D vectors.
238+
239+
// Both args are vectors and simple numeric - We can calculate the result.
240+
ReplaceNodeWithVector(
241+
crossNode,
242+
new[]
243+
{
244+
nums[0][1] * nums[1][2] - nums[1][1] * nums[0][2],
245+
nums[1][0] * nums[0][2] - nums[0][0] * nums[1][2],
246+
nums[0][0] * nums[1][1] - nums[1][0] * nums[0][1]
247+
});
248+
didChange = true;
249+
}
250+
237251
// Constant math/trig functions => <the result>
238252
var mathOp = new List<Tuple<string, Func<double, double>>>
239253
{
@@ -440,6 +454,25 @@ public static bool PerformArithmetic(this SyntaxNode rootNode)
440454
return repeatSimplifications;
441455
}
442456

457+
private static void ReplaceNodeWithVector(SyntaxNode node, ICollection<double> vectorElements)
458+
{
459+
// Squash vecN(n, n, n, ...) down to vecN(n)?
460+
var nums = vectorElements.ToList();
461+
if (nums.Count > 1 && nums.Distinct().Count() == 1)
462+
nums = new List<double> { nums.First() };
463+
464+
var bracketContent = nums.SelectMany(
465+
o => new[]
466+
{
467+
new GenericSyntaxNode(new CommaToken()),
468+
new GenericSyntaxNode(FloatToken.From(o, MaxDp).AsIntIfPossible())
469+
}).Skip(1);
470+
471+
node
472+
.ReplaceWith(new GenericSyntaxNode(new TypeToken($"vec{vectorElements.Count}")))
473+
.InsertNextSibling(new RoundBracketSyntaxNode(bracketContent));
474+
}
475+
443476
private static List<double> GetVectorNumericCsv(SyntaxNode vectorNode)
444477
{
445478
if (vectorNode.Next is not RoundBracketSyntaxNode brackets || !brackets.IsNumericCsv())

ShaderShrinker/Shrinker.Parser/SyntaxNodes/FileSyntaxNode.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -533,7 +533,7 @@ private void FindVariableDeclarations()
533533
// Create declaration node.
534534
var terminatorNode = namesAndValues.Last().Last().Next;
535535
var declNode = new VariableDeclarationSyntaxNode(typeNode);
536-
var assignments = namesAndValues.Select(o => new VariableAssignmentSyntaxNode(new GenericSyntaxNode(o.First().Token.Content), o.Count > 1 ? o.Skip(1).ToList() : null)).OfType<SyntaxNode>().ToArray();
536+
var assignments = namesAndValues.Select(o => new VariableAssignmentSyntaxNode(new GenericSyntaxNode(o.First().Token.Clone()), o.Count > 1 ? o.Skip(1).ToList() : null)).OfType<SyntaxNode>().ToArray();
537537
declNode.Adopt(assignments);
538538

539539
// Replace original nodes with the new versions.

ShaderShrinker/UnitTests/TestFiles/GolfedReference/Temple.glsl

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Processed by 'GLSL Shader Shrinker' (Shrunk by 1,417 characters)
1+
// Processed by 'GLSL Shader Shrinker' (Shrunk by 1,390 characters)
22
// (https://github.com/deanthecoder/GLSLShaderShrinker)
33

44
#define v3 vec3
@@ -149,14 +149,13 @@ vec3 ve(v3 m, v2 w) {
149149
}
150150

151151
void mainImage(out vec4 v, v2 w) {
152+
const v3 u = v3(-.59359, -.31163, .74198),
153+
Y = v3(42.563, 68.101, 59.588);
152154
v2 z = v2(0),
153155
UV = (w - .5 * iR.xy) / iR.y;
154156
v3 p,
155-
u = v3(-.59359, -.31163, .74198),
156157
M = NM(cross(v3(0, 1, 0), u)),
157158
K = NM(u + UV.x * M + UV.y * cross(u, M)),
158-
Z = v3(.42563, .68101, .59588),
159-
Y = Z * 1e2,
160159
m = v3(0);
161160
_f d = 0.;
162161
for (int steps = min(iFrame, 0); steps < 200; steps++) {
@@ -189,13 +188,13 @@ void mainImage(out vec4 v, v2 w) {
189188
else if (z.y < 3.5) {
190189
C = v3(.002, .02, .04);
191190
U = k(p, Y, 1.);
192-
_f tt = S(p);
193-
C *= 1. * (1. + SS(30., 0., tt));
194-
C *= 1. * (1. + SS(5., 0., tt));
191+
_f Z = S(p);
192+
C *= 1. * (1. + SS(30., 0., Z));
193+
C *= 1. * (1. + SS(5., 0., Z));
195194
}
196195

197-
X = max(0., dot(Z, n)) * 6.;
198-
g = .2 * max(0., dot(Z * v3(-1, 0, -1), n));
196+
X = max(0., dot(v3(.42563, .68101, .59588), n)) * 6.;
197+
g = .2 * max(0., dot(v3(-.42563, 0, -.59588), n));
199198
V = (.6 + .5 * n.y) * .05;
200199
m = C * X * v3(1.64, 1.27, .99) * U;
201200
m += C * g * v3(1.64, 1.27, .99) * H;

ShaderShrinker/UnitTests/TestFiles/SimplifiedReference/Temple.glsl

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Processed by 'GLSL Shader Shrinker' (Shrunk by 412 characters)
1+
// Processed by 'GLSL Shader Shrinker' (Shrunk by 414 characters)
22
// (https://github.com/deanthecoder/GLSLShaderShrinker)
33

44
#define SKYCOL vec3(.6, .8, .9)
@@ -142,14 +142,13 @@ vec3 vignette(vec3 col, vec2 fragCoord) {
142142
}
143143

144144
void mainImage(out vec4 fragColor, vec2 fragCoord) {
145+
const vec3 forward = vec3(-.59359, -.31163, .74198),
146+
sunPos = vec3(42.563, 68.101, 59.588);
145147
vec2 hit = vec2(0),
146148
uv = (fragCoord - .5 * iResolution.xy) / iResolution.y;
147149
vec3 p,
148-
forward = vec3(-.59359, -.31163, .74198),
149150
right = normalize(cross(vec3(0, 1, 0), forward)),
150151
rayDir = normalize(forward + uv.x * right + uv.y * cross(forward, right)),
151-
sunPosUnit = vec3(.42563, .68101, .59588),
152-
sunPos = sunPosUnit * 1e2,
153152
col = vec3(0);
154153
float d = 0.;
155154
for (int steps = min(iFrame, 0); steps < 200; steps++) {
@@ -187,8 +186,8 @@ void mainImage(out vec4 fragColor, vec2 fragCoord) {
187186
mat *= 1. * (1. + smoothstep(5., 0., terrainDist));
188187
}
189188

190-
sun = max(0., dot(sunPosUnit, n)) * 6.;
191-
back = .2 * max(0., dot(sunPosUnit * vec3(-1, 0, -1), n));
189+
sun = max(0., dot(vec3(.42563, .68101, .59588), n)) * 6.;
190+
back = .2 * max(0., dot(vec3(-.42563, 0, -.59588), n));
192191
sky = (.6 + .5 * n.y) * .05;
193192
col = mat * sun * vec3(1.64, 1.27, .99) * sha;
194193
col += mat * back * vec3(1.64, 1.27, .99) * occ;

ShaderShrinker/UnitTests/VectorArithmeticTests.cs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -266,7 +266,7 @@ public void CheckArithmeticWithFractFunction()
266266
}
267267

268268
[Test, Sequential]
269-
public void CheckArithmeticWithSingleIndexDotFunction(
269+
public void CheckArithmeticWithDotFunction(
270270
[Values("vec3 v; float f = dot(vec3(0, 0, 1), v);|vec3 v; float f = v.z;",
271271
"vec3 v; float f = dot(vec3(0, 1, 0), v);|vec3 v; float f = v.y;",
272272
"vec3 v; float f = dot(vec3(1, 0, 0), v);|vec3 v; float f = v.x;",
@@ -291,6 +291,25 @@ public void CheckArithmeticWithSingleIndexDotFunction(
291291
Assert.That(rootNode.ToCode().ToSimple(), Is.EqualTo(expected));
292292
}
293293

294+
[Test, Sequential]
295+
public void CheckArithmeticWithCrossFunction(
296+
[Values("vec3 v = cross(vec3(1, 2, 3), vec3(4, 5, 6));|vec3 v = vec3(-3, 6, -3);")] string code)
297+
{
298+
var input = code.Split('|')[0];
299+
var expected = code.Split('|')[1];
300+
301+
var lexer = new Lexer();
302+
lexer.Load(input);
303+
304+
var options = CustomOptions.None();
305+
options.PerformArithmetic = true;
306+
var rootNode = new Parser(lexer)
307+
.Parse()
308+
.Simplify(options);
309+
310+
Assert.That(rootNode.ToCode().ToSimple(), Is.EqualTo(expected));
311+
}
312+
294313
[Test, Sequential]
295314
public void CheckArithmeticWithTrigFunctions(
296315
[Values("float f = sin(3.141);",

0 commit comments

Comments
 (0)