1 | /******************************************************************************************* |
2 | * |
3 | * raylib [shaders] example - Simple shader mask |
4 | * |
5 | * This example has been created using raylib 2.5 (www.raylib.com) |
6 | * raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) |
7 | * |
8 | * Example contributed by Chris Camacho (@codifies - http://bedroomcoders.co.uk/) |
9 | * and reviewed by Ramon Santamaria (@raysan5) |
10 | * |
11 | * Copyright (c) 2019 Chris Camacho (@codifies) and Ramon Santamaria (@raysan5) |
12 | * |
13 | ******************************************************************************************** |
14 | * |
15 | * The shader makes alpha holes in the forground to give the apearance of a top |
16 | * down look at a spotlight casting a pool of light... |
17 | * |
18 | * The right hand side of the screen there is just enough light to see whats |
19 | * going on without the spot light, great for a stealth type game where you |
20 | * have to avoid the spotlights. |
21 | * |
22 | * The left hand side of the screen is in pitch dark except for where the spotlights are. |
23 | * |
24 | * Although this example doesn't scale like the letterbox example, you could integrate |
25 | * the two techniques, but by scaling the actual colour of the render texture rather |
26 | * than using alpha as a mask. |
27 | * |
28 | ********************************************************************************************/ |
29 | |
30 | #include "raylib.h" |
31 | #include "raymath.h" |
32 | |
33 | #include <stddef.h> |
34 | #include <stdint.h> |
35 | |
36 | #if defined(PLATFORM_DESKTOP) |
37 | #define GLSL_VERSION 330 |
38 | #else // PLATFORM_RPI, PLATFORM_ANDROID, PLATFORM_WEB |
39 | #define GLSL_VERSION 100 |
40 | #endif |
41 | |
42 | |
43 | #define MAXSPOT 3 // NB must be the same as define in shader |
44 | #define numStars 400 |
45 | |
46 | |
47 | // Spot data |
48 | typedef struct { |
49 | Vector2 pos; |
50 | Vector2 vel; |
51 | float inner; |
52 | float radius; |
53 | |
54 | // Shader locations |
55 | unsigned int posLoc; |
56 | unsigned int innerLoc; |
57 | unsigned int radiusLoc; |
58 | } Spot; |
59 | |
60 | // Stars in the star field have a position and velocity |
61 | typedef struct Star { |
62 | Vector2 pos; |
63 | Vector2 vel; |
64 | } Star; |
65 | |
66 | void UpdateStar(Star *s); |
67 | void ResetStar(Star *s); |
68 | |
69 | int main(void) |
70 | { |
71 | // Initialization |
72 | //-------------------------------------------------------------------------------------- |
73 | const int screenWidth = 800; |
74 | const int screenHeight = 450; |
75 | |
76 | InitWindow(screenWidth, screenHeight, "raylib - shader spotlight" ); |
77 | HideCursor(); |
78 | |
79 | Texture texRay = LoadTexture("resources/raysan.png" ); |
80 | |
81 | Star stars[numStars] = { 0 }; |
82 | |
83 | for (int n = 0; n < numStars; n++) ResetStar(&stars[n]); |
84 | |
85 | // Progress all the stars on, so they don't all start in the centre |
86 | for (int m = 0; m < screenWidth/2.0; m++) |
87 | { |
88 | for (int n = 0; n < numStars; n++) UpdateStar(&stars[n]); |
89 | } |
90 | |
91 | int frameCounter = 0; |
92 | |
93 | |
94 | |
95 | // Use default vert shader |
96 | Shader spotShader = LoadShader(0, FormatText("resources/shaders/glsl%i/spotlight.fs" , GLSL_VERSION)); |
97 | |
98 | // Get the locations of spots in the shader |
99 | Spot spots[MAXSPOT]; |
100 | /* |
101 | unsigned int posLoc; |
102 | unsigned int innerLoc; |
103 | unsigned int radiusLoc; |
104 | */ |
105 | for (int i = 0; i < MAXSPOT; i++) |
106 | { |
107 | char posName[32] = "spots[x].pos\0" ; |
108 | char innerName[32] = "spots[x].inner\0" ; |
109 | char radiusName[32] = "spots[x].radius\0" ; |
110 | |
111 | posName[6] = '0' + i; |
112 | innerName[6] = '0' + i; |
113 | radiusName[6] = '0' + i; |
114 | |
115 | spots[i].posLoc = GetShaderLocation(spotShader, posName); |
116 | spots[i].innerLoc = GetShaderLocation(spotShader, innerName); |
117 | spots[i].radiusLoc = GetShaderLocation(spotShader, radiusName); |
118 | |
119 | } |
120 | |
121 | // tell the shader how wide the screen is so we can have |
122 | // a pitch black half and a dimly lit half. |
123 | { |
124 | unsigned int wLoc = GetShaderLocation(spotShader, "screenWidth" ); |
125 | float sw = (float)GetScreenWidth(); |
126 | SetShaderValue(spotShader, wLoc, &sw, UNIFORM_FLOAT); |
127 | } |
128 | |
129 | // randomise the locations and velocities of the spotlights |
130 | // and initialise the shader locations |
131 | for (int i = 0; i < MAXSPOT; i++) |
132 | { |
133 | |
134 | spots[i].pos.x = GetRandomValue(64, screenWidth - 64); |
135 | spots[i].pos.y = GetRandomValue(64, screenHeight - 64); |
136 | spots[i].vel = (Vector2){ 0, 0 }; |
137 | |
138 | while ((fabs(spots[i].vel.x) + fabs(spots[i].vel.y)) < 2) |
139 | { |
140 | spots[i].vel.x = GetRandomValue(-40, 40)/10.0; |
141 | spots[i].vel.y = GetRandomValue(-40, 40)/10.0; |
142 | } |
143 | |
144 | spots[i].inner = 28 * (i + 1); |
145 | spots[i].radius = 48 * (i + 1); |
146 | |
147 | SetShaderValue(spotShader, spots[i].posLoc, &spots[i].pos.x, UNIFORM_VEC2); |
148 | SetShaderValue(spotShader, spots[i].innerLoc, &spots[i].inner, UNIFORM_FLOAT); |
149 | SetShaderValue(spotShader, spots[i].radiusLoc, &spots[i].radius, UNIFORM_FLOAT); |
150 | } |
151 | |
152 | SetTargetFPS(60); // Set to run at 60 frames-per-second |
153 | //-------------------------------------------------------------------------------------- |
154 | |
155 | // Main game loop |
156 | while (!WindowShouldClose()) // Detect window close button or ESC key |
157 | { |
158 | // Update |
159 | //---------------------------------------------------------------------------------- |
160 | frameCounter++; |
161 | |
162 | // Move the stars, resetting them if the go offscreen |
163 | for (int n = 0; n < numStars; n++) UpdateStar(&stars[n]); |
164 | |
165 | // Update the spots, send them to the shader |
166 | for (int i = 0; i < MAXSPOT; i++) |
167 | { |
168 | if ( i == 0 ) { |
169 | Vector2 mp = GetMousePosition(); |
170 | spots[i].pos.x = mp.x; |
171 | spots[i].pos.y = screenHeight - mp.y; |
172 | } else { |
173 | spots[i].pos.x += spots[i].vel.x; |
174 | spots[i].pos.y += spots[i].vel.y; |
175 | |
176 | if (spots[i].pos.x < 64) spots[i].vel.x = -spots[i].vel.x; |
177 | if (spots[i].pos.x > screenWidth - 64) spots[i].vel.x = -spots[i].vel.x; |
178 | if (spots[i].pos.y < 64) spots[i].vel.y = -spots[i].vel.y; |
179 | if (spots[i].pos.y > screenHeight - 64) spots[i].vel.y = -spots[i].vel.y; |
180 | } |
181 | |
182 | SetShaderValue(spotShader, spots[i].posLoc, &spots[i].pos.x, UNIFORM_VEC2); |
183 | } |
184 | |
185 | // Draw |
186 | //---------------------------------------------------------------------------------- |
187 | BeginDrawing(); |
188 | |
189 | ClearBackground(DARKBLUE); |
190 | |
191 | // Draw stars and bobs |
192 | for (int n = 0; n < numStars; n++) |
193 | { |
194 | // Single pixel is just too small these days! |
195 | DrawRectangle(stars[n].pos.x, stars[n].pos.y, 2, 2, WHITE); |
196 | } |
197 | |
198 | for (int i = 0; i < 16; i++) |
199 | { |
200 | DrawTexture(texRay, |
201 | (screenWidth/2.0) + cos((frameCounter + i*8)/51.45f)*(screenWidth/2.2) - 32, |
202 | (screenHeight/2.0) + sin((frameCounter + i*8)/17.87f)*(screenHeight/4.2), |
203 | WHITE); |
204 | } |
205 | |
206 | // Draw spot lights |
207 | BeginShaderMode(spotShader); |
208 | // instead of a blank rectangle you could render here |
209 | // a render texture of the full screen used to do screen |
210 | // scaling (slight adjustment to shader would be required |
211 | // to actually pay attention to the colour!) |
212 | DrawRectangle(0,0,screenWidth,screenHeight,WHITE); |
213 | EndShaderMode(); |
214 | |
215 | DrawFPS(10, 10); |
216 | |
217 | DrawText("Move the mouse!" , 10, 30, 20, GREEN); |
218 | DrawText("Pitch Black" , screenWidth * .2, screenHeight / 2, 20, GREEN); |
219 | DrawText("Dark" , screenWidth * .66, screenHeight / 2, 20, GREEN); |
220 | |
221 | |
222 | EndDrawing(); |
223 | //---------------------------------------------------------------------------------- |
224 | } |
225 | |
226 | // De-Initialization |
227 | //-------------------------------------------------------------------------------------- |
228 | UnloadTexture(texRay); |
229 | |
230 | CloseWindow(); // Close window and OpenGL context |
231 | //-------------------------------------------------------------------------------------- |
232 | |
233 | return 0; |
234 | } |
235 | |
236 | |
237 | void ResetStar(Star *s) |
238 | { |
239 | s->pos = (Vector2){ GetScreenWidth()/2.0f, GetScreenHeight()/2.0f }; |
240 | |
241 | do |
242 | { |
243 | s->vel.x = (float)GetRandomValue(-1000, 1000)/100.0f; |
244 | s->vel.y = (float)GetRandomValue(-1000, 1000)/100.0f; |
245 | |
246 | } while (!(fabs(s->vel.x) + fabs(s->vel.y) > 1)); |
247 | |
248 | s->pos = Vector2Add(s->pos, Vector2MultiplyV(s->vel, (Vector2){ 8, 8 })); |
249 | } |
250 | |
251 | void UpdateStar(Star *s) |
252 | { |
253 | s->pos = Vector2Add(s->pos, s->vel); |
254 | |
255 | if (s->pos.x < 0 || s->pos.x > GetScreenWidth() || |
256 | s->pos.y < 0 || s->pos.y > GetScreenHeight()) |
257 | { |
258 | ResetStar(s); |
259 | } |
260 | } |
261 | |
262 | |
263 | |