Juggler
This sample displays a juggling pattern computed entirely in a shader.
3 Balls
5 Balls
7 Balls
9 Balls
Animate
// The 4x4 world view projection matrix. float4x4 worldViewProjection : WORLDVIEWPROJECTION; float theta; float num; // input parameters for our vertex shader struct VertexShaderInput { float4 position : POSITION; float2 texCoord : TEXCOORD0; }; // input parameters for our pixel shader // also the output parameters for our vertex shader struct PixelShaderInput { float4 position : POSITION; float2 texCoord : TEXCOORD0; float4 color : COLOR; }; /** * vertexShaderMain - our vertex shader for the juggling texture */ PixelShaderInput vertexShaderMain(VertexShaderInput input) { PixelShaderInput output; output.position = mul(input.position, worldViewProjection); output.texCoord = 4.0 * (input.texCoord - float2(0.5, 0.5)); output.color = float4(1, 1, 1, 1); return output; } float myLength(in float2 v) { return dot(v, v); } // Draw the balls in a single arc. bool drawBall(in float t, in float pi, in float4 offset, in float num, in float2 source_hand, in float2 dest_hand, in float height_factor, in float baseline, in float ball_radius_2, in float hand_throw_offset, in float2 Z) { // Here map t from its current range of [0, 2 * num * pi) onto // [0, (num - 1) * pi] via adding an offset and modding, then map that to // [0, 1) via scaling. The first mapping tells us where in the repeating // cycle we are, and the second mapping simplifies the calculation of the // parabola. // The vector offset is used to distinguish between balls in the same arc, but // out of phase. At the beginning of this function, all the operations are // vectorized to save instructions; we get to calculate 4 ball positions for // the price of 1. Then at the end we have to split out the results to do // the final checks. float4 time = fmod(t + offset * pi, ((num - 1) * pi)) / (num - 1) / pi; float dx = dest_hand.x - source_hand.x; float4 x = time * dx + source_hand.x - hand_throw_offset; float4 y = -(time - 0.5) * (time - 0.5) + 0.25; y = y * height_factor + baseline; float4 ZX = Z.x; float4 ZY = Z.y; float4 len_2 = (ZX - x) * (ZX - x) + (ZY - y) * (ZY - y); if (len_2.x < ball_radius_2) { return true; } if (len_2.y < ball_radius_2) { return true; } if (len_2.z < ball_radius_2) { return true; } if (len_2.w < ball_radius_2) { return true; } return false; } bool drawBallPair(in float t, in float pi, in float4 offset, in float num, in float2 right_hand, in float2 left_hand, in float height_factor, in float baseline, in float ball_radius_2, in float hand_swing_radius, in float2 Z) { // Right-to-left balls. if (drawBall(t, pi, offset, num, right_hand, left_hand, height_factor, baseline, ball_radius_2, hand_swing_radius, Z)) { return true; } // Left-to-right balls. if (drawBall(t, pi, offset + 1, num, left_hand, right_hand, height_factor, baseline, ball_radius_2, -hand_swing_radius, Z)) { return true; } return false; } /** * pixelShaderMain - pixel shader */ float4 pixelShaderMain(PixelShaderInput input) : COLOR { const float pi = 3.14159265; const float baseline = -1.2; const float2 right_hand = float2(0.8, baseline); const float2 left_hand = float2(-0.8, baseline); const float hand_swing_radius = 0.3; const float hand_radius_2 = 0.15 * 0.15; const float ball_radius_2 = 0.1 * 0.1; const float4 ball_color = float4(1, 1, 1, 1); const float4 background_color = float4(0, 0, 0, 1); float height_factor = num; float2 Z = input.texCoord; // Coerce to the range [0, g_numBalls * 2 Pi]. float t = fmod(theta, 2 * pi * num); float2 r_h = hand_swing_radius * float2(-cos(t), sin(t)) + right_hand; float2 l_h = hand_swing_radius * float2(-cos(t), -sin(t)) + left_hand; // Draw the hands. if (myLength(Z - r_h) < hand_radius_2 && Z.y < r_h.y) return float4(1, 0, 0, 1); else if (myLength(Z - l_h) < hand_radius_2 && Z.y < l_h.y) return float4(0, 0, 1, 1); // Draw the balls in the hands. int phase = floor(t / pi); if (fmod((float)phase, (float)2) == 1) { if (myLength(Z - r_h) < ball_radius_2) return ball_color; } else { if (myLength(Z - l_h) < ball_radius_2) return ball_color; } float4 offset = float4(0, 2, 4, 6); if (drawBallPair(t, pi, offset, num, right_hand, left_hand, height_factor, baseline, ball_radius_2, hand_swing_radius, Z)) { return ball_color; } // In theory we could just keep adding to the offset and calling drawBallPair // here, but we run out of registers and instructions pretty quickly. return background_color; } // Here we tell our effect file *which* functions are // our vertex and pixel shaders. // #o3d VertexShaderEntryPoint vertexShaderMain // #o3d PixelShaderEntryPoint pixelShaderMain // #o3d MatrixLoadOrder RowMajor