Jump to content
  • Advertisement
Sign in to follow this  
  • entries
    16
  • comments
    8
  • views
    6645

Entries in this blog

 

Effect: Area light shadows (Pt. 1 - PCSS)

Welcome to the first part of multiple effect articles about soft shadows. In recent days I've been working on area light support in my own game engine, which is critical for one of the game concepts I'd like to eventually do (if time will allow me to do so). For each area light it is crucial to have proper soft shadows with proper penumbra. For motivation, let's have the following screenshot with 3 area lights with various sizes: Fig. 01 - PCSS variant that allows for perfectly smooth, large-area light shadows Let's start the article by comparison of the following 2 screenshots - one with shadows and one without: Fig. 02 - Scene from default viewpoint lit with light without any shadows (left) and with shadows (right) This is the scene we're going to work with, and for the sake of simplicity all of the comparison screenshots will be from this exactly same viewpoint with 2 different scene configurations. Let's start with definition of how shadows are created. Given a scene and light which we're viewing. Shadow umbra will be present at each position where there is no direct visibility between given position and any existing point on the light. Shadow penumbra will be present at each position where there is visibility of any point on the light, yet not all of them. No shadow is everywhere where there is full direct visibility between each point on the light and position. Most of the games tend to simplify, instead of defining a light as area or volume, it gets defined as infinitely small point, this gives us few advantages: For single point it is possible to define visibility in binary way - either in shadow, or not in shadow From single point, a projection of the scene can be easily constructed in such way, that definition of shadow becomes trivial (either position is occluded by other objects in scene from lights point of view, or it isn't) From here, one can follow into the idea of shadow mapping - which is a basic technique for all others used here. Standard Shadow Mapping Trivial, yet should be mentioned here. inline float ShadowMap(Texture2D<float2> shadowMap, SamplerState shadowSamplerState, float3 coord) { return shadowMap.SampleLevel(shadowSamplerState, coord.xy, 0.0f).x < coord.z ? 0.0f : 1.0f; } Fig. 03 - code snippet for standard shadow mapping,where depth map (stored 'distance' from lights point of view) is compared against calculated 'distance' between point we're computing right now and given light position. Word 'distance' may either mean actual distance, or more likely just value on z-axis for light point of view basis. Which is well known to everyone here, giving us basic results, that we all well know, like: Fig. 04 - Standard Shadow Mapping This can be simply explained with following image: Fig. 05 - Each rendered pixel calculates whether its 'depth' from light point is greater than what is written in 'depth' map from light point (represented as yellow dot), white lines represent computation for each pixel. Percentage-Close-Filtering (PCF) To make shadow more visually appealing, adding soft-edge is a must. This is done by simply performing NxN tests with offsets. For the sake of improved visual quality I've used shadow mapping with bilinear filter (which requires resolving 4 samples), along with 5x5 PCF filtering: Fig. 06 - Percentage close filtering (PCF) results in nice soft-edged shadows, sadly the shadow is uniformly soft everywhere Clearly, none of the above techniques does any penumbra/umbra calculation, and therefore they're not really useful for area lights. For the sake of completeness, I'm adding basic PCF source code (for the sake of optimization, feel free to improve for your uses): inline float ShadowMapPCF(Texture2D<float2> tex, SamplerState state, float3 projCoord, float resolution, float pixelSize, int filterSize) { float shadow = 0.0f; float2 grad = frac(projCoord.xy * resolution + 0.5f); for (int i = -filterSize; i <= filterSize; i++) { for (int j = -filterSize; j <= filterSize; j++) { float4 tmp = tex.Gather(state, projCoord.xy + float2(i, j) * float2(pixelSize, pixelSize)); tmp.x = tmp.x < projCoord.z ? 0.0f : 1.0f; tmp.y = tmp.y < projCoord.z ? 0.0f : 1.0f; tmp.z = tmp.z < projCoord.z ? 0.0f : 1.0f; tmp.w = tmp.w < projCoord.z ? 0.0f : 1.0f; shadow += lerp(lerp(tmp.w, tmp.z, grad.x), lerp(tmp.x, tmp.y, grad.x), grad.y); } } return shadow / (float)((2 * filterSize + 1) * (2 * filterSize + 1)); } Fig. 07 - PCF filtering source code Representing this with image: Fig. 08 - Image representing PCF, specifically a pixel with straight line and star in the end also calculates shadow in neighboring pixels (e.g. performing additional samples). The resulting shadow is then weighted sum of the results of all the samples for given pixel. While the idea is quite basic, it is clear that using larger kernels would end up in slow computation. There are ways how to perform separable filtering of shadow maps using different approach to resolve where the shadow is (Variance Shadow Mapping for example). They do introduce additional problems though. Percentage-Closer Soft Shadows To understand problem in both previous techniques let's replace point light with area light in our sketch image. Fig. 09 - Using Area light introduces penumbra and umbra. The size of penumbra is dependent on multiple factors - distance between receiver and light, distance between blocker and light and light size (shape). To calculate plausible shadows like in the schematic image, we need to calculate distance between receiver and blocker, and distance between receiver and light. PCSS is a 2-pass algorithm that does calculate average blocker distance as the first step - using this value to calculate penumbra size, and then performing some kind of filtering (often PCF, or jittered-PCF for example). In short, PCSS computation will look similar to this: float ShadowMapPCSS(...) { float averageBlockerDistance = PCSS_BlockerDistance(...); // If there isn't any average blocker distance - it means that there is no blocker at all if (averageBlockerDistance < 1.0) { return 1.0f; } else { float penumbraSize = estimatePenumbraSize(averageBlockerDistance, ...) float shadow = ShadowPCF(..., penumbraSize); return shadow; } } Fig. 10 - Pseudo-code of PCSS shadow mapping The first problem is to determine correct average blocker calculation - and as we want to limit search size for average blocker, we simply pass in additional parameter that determines search size. Actual average blocker is calculated by searching shadow map with depth value smaller than of receiver. In my case I used the following estimation of blocker distance: // Input parameters are: // tex - Input shadow depth map // state - Sampler state for shadow depth map // projCoord - holds projection UV coordinates, and depth for receiver (~further compared against shadow depth map) // searchUV - input size for blocker search // rotationTrig - input parameter for random rotation of kernel samples inline float2 PCSS_BlockerDistance(Texture2D<float2> tex, SamplerState state, float3 projCoord, float searchUV, float2 rotationTrig) { // Perform N samples with pre-defined offset and random rotation, scale by input search size int blockers = 0; float avgBlocker = 0.0f; for (int i = 0; i < (int)PCSS_SampleCount; i++) { // Calculate sample offset (technically anything can be used here - standard NxN kernel, random samples with scale, etc.) float2 offset = PCSS_Samples[i] * searchUV; offset = PCSS_Rotate(offset, rotationTrig); // Compare given sample depth with receiver depth, if it puts receiver into shadow, this sample is a blocker float z = tex.SampleLevel(state, projCoord.xy + offset, 0.0f).x; if (z < projCoord.z) { blockers++; avgBlockerDistance += z; } } // Calculate average blocker depth avgBlocker /= blockers; // To solve cases where there are no blockers - we output 2 values - average blocker depth and no. of blockers return float2(avgBlocker, (float)blockers); } Fig. 11 - Average blocker estimation for PCSS shadow mapping For penumbra size calculation - first - we assume that blocker and receiver are plannar and parallel. This makes actual penumbra size is then based on similar triangles. Determined as: penmubraSize = lightSize * (receiverDepth - averageBlockerDepth) / averageBlockerDepth This size is then used as input kernel size for PCF (or similar) filter. In my case I again used rotated kernel samples. Note.: Depending on the samples positioning one can achieve different area light shapes. The result gives quite correct shadows, with the downside of requiring a lot of processing power to do noise-less shadows (a lot of samples) and large kernel sizes (which also requires large blocker search size). Generally this is very good technique for small to mid-sized area lights, yet large-sized area lights will cause problems. Fig. 12 - PCSS shadow mapping in practice As currently the article is quite large and describing 2 other techniques which I allow in my current game engine build (first of them is a variant of PCSS that utilizes mip maps and allows for slightly larger light size without impacting the performance that much, and second of them is sort of back-projection technique), I will leave those two for another article which may eventually come out. Anyways allow me to at least show a short video of the first technique in action:        

Vilem Otte

Vilem Otte

 

Hint: Version Control

Sooner or later I wanted to do at least a bit of posting about version control systems which I've worked with and which one of those I find the best for me. In past years I've worked with various version control systems, with various interfaces. I personally have few years of experience with both, SVN and GIT - which both I used (and still regularly use) for various projects. The named are most famous ones, and personally I like and prefer git a bit more for having a lot of advantages over svn (and yeah, git has better memes - might be important too!).   For quite long I've stored some of my personal, or conceptual projects just on drive and copied it back and forth (not just old ones, incl. few very recent ones!). Knowing the advantage of version control (and actually having some interesting projects archived on my own Github account) I said to myself that it's definitely time to change this. Now, as these projects are internal and I don't like paying Github for service, that can run on the server(s) which I'm already paying, I finally installed git - yet I was hunting for some visual interface to be able to see what goes on, add issues (which serve often also as to-do list for me), etc.   Out of the user interfaces one can host himself, I've used GitLab (which can do a lot more than I require - including CI, CD, etc.), which is perfectly suitable for the job, and it even exists as package in Ubuntu! There is one huge downside - it is extremely HEAVY (at least for the feature set I require). I indeed set it up, but I didn't like how much resources it used, despite actually not doing anything and working with just single user at that point. Gitlab is huge, it has all the feature one can wish for. But after hours burnt on setting it up to make it work properly ... I asked myself, isn't there something better, that could be light, better fit for my simple use cases and easy to setup? At this point I've searched and found GOGS, which is very simple to use (compared to GitLab, which often requires quite some time to setup - especially on servers running Apache), small, fast, etc. I couldn't find any real disadvantage to it. Did the 5 minute setup and here I go... I was amazed, this hasn't happened to me in software for well... at least few months. How easy this was to setup in the end - and how fast (anything is opened within 100ms).   This brings me to: On one side, I admire complex and large software, like GitLab. It can do a TON of things, and it is very good in all of them. You want automatic-build and deployment each time you merge into master? You can set it up. You want strict permissions settings? You can set those. And so on... On the other side, well I'm fan of simple software. That can be installed fast, used quickly without additional hours of setup and that is fast. I've switched from KDE to XFCE on my Linux boxes for exactly this reason. Sometimes less is more...

Vilem Otte

Vilem Otte

 

Effect: Black hole background

For one of the upcoming projects (which will follow in some of the following posts), and as it had to fit the lore of the game, a black hole was necessary. Before going forward - let me add an image of what result I want to achieve: Artist conception of black hole - NASA/JPL-Caltech While the image is similar to the final effect I wanted to achieve, I did some changes on the effect to be more colorful and bright - but the original idea was from this image. The effect is actually separated in 3 major parts - Disk, Streaks and Post processing.Of course there is also the core of the black hole (which is just a small sphere, with black color). Disk The disk around black hole is actually just matter that rotates around the core. Depending on the distance from the core the average density will increase near the event horizon and decrease further from it. Near the core the density can be so high that it may eventually have temperature close to star - therefore there might be high emissive energy - and therefore light. Also, due to the time dilation (and therefore light having hard time escaping near the event horizon), the emissivity is getting lower very close near the event horizon. Anything beyond event horizon is invisible from the outside, because gravity there is so strong, not even photons can escape. At least that is what I understand from the topic from physics point of view. This actually explained what can be seen on the image and what is going on graphically, that is: The disk rotates around the core The density of disk decreases further from the core The emissive light decreases further from the core, and therefore some (outer) parts of the disk will be lit by inner part ... although inner part around the core has to be somehow darker Which can be solved with simple texturing and some basic lighting of the result. Using whirl-like texture as a basis proved to be a good start for me. I started off by creating a whirl-like texture that would define density in various parts of the disk, which resulted in this: Generating a normal map for lighting from this is actually quite straight forward (and easy in Substance Designer F.e.) - and after short time, I also had a normal map: Putting just these together with basic diffuse lighting (standard N.L) from the center (slightly above the plane) gives us some basic results: Next thing is defining emissivity. This is done simply by using 1D gradient texture for which the coordinate will be distance from the center. The gradient I came up with is: Notice the left part - which is near the event horizon.will give us similar feeling to the image as we're not jumping straight to bright value. Applying emissive value (as both - multiplier for the color, and as emission) gives us this look: Which looks good enough already - I personally played a bit with values (mainly playing with contrast and other multiplication factors - F.e. for alpha channel/transparency), and ended up with this result: Resulting pixel shader is as simple as: fixed4 frag (v2f i) : SV_Target { // Calculate texture coordinate for gradient float2 centric = i.uv * 2.0f - 1.0f; float dist = min(sqrt(centric.x * centric.x + centric.y * centric.y), 1.0f); // Lookup gradient float3 gradient = tex2D(_GradientTex, float2(dist, 0.0f)).xyz; // Light direction (hack - simulates light approx. in the middle, slightly pushed up) float3 lightDir = normalize(float3(centric.x, -centric.y, -0.5f)); // Use normals from normal map float3 normals = normalize(tex2D(_NormalsTex, i.uv).xyz * 2.0f - 1.0f); // Simple N.L is enough for lighting float bump = max(dot(-lightDir, normals), 0.0f); // Alpha texture float alpha = tex2D(_AlphaTex, i.uv).x; // Mix colors (note. contrast increase required for both - lighting and alpha) return fixed4((gradient * bump * bump * bump + gradient) * 0.75f, min(alpha * alpha * 6.0f, 1.0f)); } Streaks There are 2 streaks, directing upwards and downwards from the core. My intention was to make them bright compared to the core and blue-ish - to keep the background more colorful in the end. Each streak is composed from 2 objects, a very bright white sphere (which will take advantage of used post processing effects to feel bright), and a geometry for the streaks (instead of using particles). The geometry is quite simple - looks a bit like rotated and cut hyperbole, notice the UV map on the left (it is important for understanding the next part): This geometry is there 4 times for each direction of the streak, rotated around the origin by 90, 180 and 270 degrees. The actual idea for streaks was simple - have a simple geometry of cut surface, and roll a texture over it. Multiplying with correct color and distance from the beginning of the streak adds color effect that nicely fades into the background. To create a particles-like texture that varies in intensity I used Substance Designer again and come up with: By simply applying this texture as alpha, and moving the X-texture coordinate the streak is animated, like: Multiplying by wanted color gives us: And multiplying by factor given by distance from the origin of the streak results in: Which is actually quite acceptable for me. For the sake of completeness, here is the full pixel shader: fixed4 frag (v2f i) : SV_Target { // Texture coordinates, offset based on external value (animates streaks) float2 uv = i.uv.xy + float2(_Offset, 0.0f); // Alpha texture for streaks fixed alpha = tex2D(_AlphaTex, uv); // Distance from origin factor (calculated from texture coordinates of streaks) float factor = pow(1.0f - i.uv.x, 4.0f); // Multiplication factor (to 'overbright' the effect - so that it 'blooms properly' when applying post-process) float exposure = 6.0f; // Apply resulting color return fixed4(exposure * 51.0 / 255.0, exposure * 110.0 / 255.0, exposure * 150.0 / 255.0, alpha * factor); } Putting the effects together ends up in: Post Processing By using simple bloom effect, we can achieve the resulting final effect as shown in video, which improves this kind of effect a lot. I've added lens dirt texture to bloom. We need to be careful with the actual core - as that needs to stay black (I intentionally let it stay black even through the bloom). You can do this either by using floating-point render target before the bloom and write some low value instead of black (careful with tone mapping though - yet you might want to go even for negative numbers), or just render the core after the bloom effect. The resulting effect looks like: And as promised - a video showing the effect:

Vilem Otte

Vilem Otte

 

Engine: Physical lights, plausible shadows, etc.

As I have quite a bit more to share, I've decided to divide this post in 2 parts. The first one will do a brief explanation on physical lights, while the second will focus on plausible shadows (which are my first step towards area lighting). Physical Lights Are a nice to have feature. Instead of specifying intensity and color with some arbitrary values - one specifies luminous power (with lm), temperature of light source and an arbitrary value of color. Fig. 1 - From left to right - Tungsten bulb with 500W and 1000W, Simulated point light with temperature of sun and intensity of 10k lumens, Simulated point light with temperature of overcast sky and intensity of 20k lumens. Tone mapping was enabled. Using lumens to describe intensity of point/spot lights, and temperature to describe colors allows to simulate various lights based on their actual parameters. To allow for lights with additional colors (red, green, etc.), additional color parameter is introduced. /// <summary>Convert temperature of black body into RGB color</summary> /// <param name="temperature">Temperature of black body (in Kelvin)</param> Engine::float4 TemperatureToColor(float temperature) { float tmp = temperature / 100.0f; Engine::float4 result; if (tmp <= 66.0f) { result.x = 255.0f; result.y = tmp; result.y = 99.4708025861f * log(result.y) - 161.1195681661f; if (tmp <= 19.0f) { result.z = 0.0f; } else { result.z = tmp - 10.0f; result.z = 138.5177312231f * log(result.z) - 305.0447927307f; } } else { result.x = tmp - 60.0f; result.x = 329.698727446f * pow(result.x, -0.1332047592f); result.y = tmp - 60.0f; result.y = 288.1221695283f * pow(result.y, -0.0755148492f); result.z = 255.0f; } return result / 255.0f; } Fig. 2 - Snippet for calculating color from temperature. I intentionally missed one thing - attenuation - which is very important to make all of this work properly. The main reason to do so was that I'd like to talk about it shortly when I finish my work on area lights. Plausible Shadows Speaking of area lights, the main challenge in realtime rendering and area lights are definitely shadows. As of today I haven't seen any game doing area lights shadows properly. Most of them either don't cast shadows at all, or use a cheap shadow map, possibly with standard NxN filtering (some do precompute light map, which often suffers on poor resolution - and doesn't allow shadow casting from dynamic objects). Before I'm going to finish my implementation of area lights I have to attempt to implement so solid shadowing technique for area lights that works properly with dyanmic objects. First of all, I had to switch from shadow map per light to a solution that has one huge texture where ALL lights that casts shadows render their shadow maps into. This is a necessary requirement for me to easily be able to cast shadows from all lights during F.e. GI computation. On the other hand this makes filtering a bit more tricky, especially for more specific filters. There are still some minor problems with my approach including: Proper claming Removing seams for point lights shadows Those haven't stopped me from trying to implement nice looking PCSS (Percentage Closer Soft Shadows), and compare against standard PCF (Percentage Close Filtering) in terms of quality. Fig. 3 - Left PCSS, Right PCF - While PCSS does look indeed like shadows from area light source, it is far from perfect mainly due to noise and still somehow limited light size. For convenience I'm adding source code for my PCSS. I've taken some of the values from Unity's implementation of PCSS that seems to picked the values quite well. Reference: https://github.com/TheMasonX/UnityPCSS inline float PCSS_Noise(float3 location) { float3 skew = location + 0.2127f + location.x * location.y * location.z * 0.3713f; float3 rnd = 4.789f * sin(489.123f * (skew)); return frac(rnd.x * rnd.y * rnd.z * (1.0 + skew.x)); } inline float2 PCSS_Rotate(float2 pos, float2 rotation) { return float2(pos.x * rotation.x - pos.y * rotation.y, pos.y * rotation.x + pos.x * rotation.y); } inline float2 PCSS_BlockerDistance(Texture2D<float2> tex, SamplerState state, float3 projCoord, float searchUV, float2 rotation) { int blockers = 0; float avgBlockerDistance = 0.0f; for (int i = 0; i < (int)PCSS_SampleCount; i++) { float2 offset = PCSS_Samples[i] * searchUV; offset = PCSS_Rotate(offset, rotation); float z = tex.SampleLevel(state, projCoord.xy + offset, 0.0f).x; if (z < projCoord.z) { blockers++; avgBlockerDistance += z; } } avgBlockerDistance /= blockers; return float2(avgBlockerDistance, (float)blockers); } inline float PCSS_PCFFilter(Texture2D<float2> tex, SamplerState state, float3 projCoord, float filterRadiusUV, float penumbra, float2 rotation, float2 grad) { float sum = 0.0f; for (int i = 0; i < (int)PCSS_SampleCount; i++) { float2 offset = PCSS_Samples[i] * filterRadiusUV; offset = PCSS_Rotate(offset, rotation); sum += tex.SampleLevel(state, projCoord.xy + offset, 0.0f).x < projCoord.z ? 0.0f : 1.0f; } sum /= (float)PCSS_SampleCount; return sum; } inline float ShadowMapPCSS(Texture2D<float2> tex, SamplerState state, float3 projCoord, float resolution, float pixelSize, float lightSize) { float2 uv = projCoord.xy; float depth = projCoord.z; float zAwareDepth = depth; float rotationAngle = Random(projCoord.xy) * 3.1415926; float2 rotation = float2(cos(rotationAngle), sin(rotationAngle)); float searchSize = lightSize * saturate(zAwareDepth - .02) / zAwareDepth; float2 blockerInfo = PCSS_BlockerDistance(tex, state, projCoord, searchSize, rotation); if (blockerInfo.y < 1.0) { return 1.0f; } else { float penumbra = max(zAwareDepth - blockerInfo.x, 0.0); float filterRadiusUV = penumbra * lightSize; float2 grad = frac(projCoord.xy * resolution + 0.5f); float shadow = PCSS_PCFFilter(tex, state, projCoord, filterRadiusUV, penumbra, rotation, grad); return shadow; } } Fig. 4 - PCSS source code To allow for more smooth and less noisy shadows I've tried to think off a way using mip-mapped texture atlas. And while having additional problems (possibly solvable), it is possible to achieve very smooth nice looking noise-less penumbrae shadows. Fig. 5 - Soft penumbrae shadows from my attempt. This is probably all for today from me. If possible I'd like to dig a bit more into shadows and area lights next time, but who knows - I might get attracted by something completely different. Thanks for reading!
 

Structure Changes

So I've decided to be a bit more productive on writing side here on GameDev, and change form of my contributions here to something more consistent and possibly less boring. Why am I doing that? In short, to keep my motivation ... In long, unless I properly motivate myself and write down a task what I want to do and when it has to be finished - I tend to get distracted by literally everything, I've found that writing everything down suits me quite well in my work - so I'm attempting to apply similar pattern to my hobby projects. For start, I'd like to aim to post 3 times a week on average focused on 3 different things: Game Engine/Tools Development

This one will be mainly showing some progress or specific feature and its debugging. For me, this is one of the biggest tools I've made myself completely alone.
  Game Development

Basically something like 'weekly-report' from development of one game that I'm working at the time. I'm going to release some of them, and possibly talk about stats if it is going to be played at least by someone.
  Crazy Stuff

Each week I decide to try some short concept or something, or calculate something. These might even be non-gamedev related, yet somehow I consider those worth sharing somewhere (incl. code snippets)... or even just math, who knows. As this one was very boring post without any image or photo, let me add at least a photo of my new family member, who constantly attempts to attack my hands on keyboard:  
 

Ludum Dare 41

It wouldn't be me if I wasn't participating actively in Ludum Dare. This time around I went for first person shooter, with sort of mysteries/physics attached to it. Before going further in the article - if you wish you can play it. Take a look here - https://ldjam.com/events/ludum-dare/41/logicatory  And if you're too lazy to play - you can watch here: Logicatory Yup that's the name. This time I went again with Unity, as I'm successfully avoiding using my own game engine. For asset creation I used Gimp, Blender, Substance Painter, Substance Designer ... and that's it! This time around I went solo, two of my friends who were thinking about joining changed their plans in last moment - one went just for compo and another one had major change of plans for the weekend. So, I was balancing between teaming up with somebody more random on Ludum Dare, but in the end I decided to go solo. To check whether I still can do it in one man. I went for jam, as regularly, because I really enjoy having that 1 additional day for polishing art and finishing up the game a bit more. What went right? The game concept, prototype development and some part of art. Strictly speaking I had an idea for game concept really early, upon waking up, and I could start working on it. The prototype was finished in matter of few hours, which was awesome - I could run around the map, shoot, bunny-hop - this really boosted my morale. I believe I did well with art, of course most of the guys here, who are actual artists can do a lot better - for me, I just simply like making art, even though I'm terrible at it. I wouldn't dare to include my art in commercial game project, but for Ludum Dare it is perfect - programmer's art. Level design, even though having just one simple level, I think I did my job well to keep player most of the time busy (for those few minutes of gameplay). While it is short, I believe it is a lot better than having long and repetitive game. As this way everyone will finish your game - which is always huge advantage on Ludum Dare. What went wrong? Simply said, Monday. I had to work for most of that day, and it took out large part of my time. When I finished, quite late in the evening, I was seriously thinking about giving up. At that moment I told myself a typical phrase: "GIT GUD" ... and finished it. Cut down the level in half. Added last-minute assets. Added music on last minute, built it and released for Ludum Dare. Thinking about it, this actually should be part of what went right, because if I wouldn't push myself - I would never finish it. So what actually went wrong was light-map baking. I wanted to try light maps from Unity and attempted it, they were computing in the end for four hours in highest quality. I still managed to do this before submission hour. Conclusion I enjoyed Ludum Dare this time, a bit less than usual due to work on Monday, but such is life. I've learned something new and tried something I wanted to try for quite some time - so for me this was a success. I'm very curious to check and play entries from other participants, and looking forward to read their opinions on Logicatory too! Next time, I'll be back with something more interesting!

Vilem Otte

Vilem Otte

 

Shadows

I was actually trying to avoid this topic as long as possible, but with my current updates on lighting system - I had to bump into it, and of course write about it. So, when designing my lighting system I kept in mind few things I want to achieve with it - multiple light sources, that is a must have (especially when you're using deferred shading), all of those need to be used throughout the stages (global illumination, reflections, etc.), and I require all lighting to be dynamic and capable of casting shadows. Other than that multiple supported light types - point, spot, directional and area light sources, all supporting casting shadows. While the lighting system is still not complete, shadows pretty much are. Or to be precise shadow filtering, a user needs to be able to select what shadow filtering method is light going to use (depending on the light importance, range and strength). So let's take a look at few examples of shadow filters available: Standard shadow mapping & PCF shadow mapping Bilinear shadow mapping & PCF + Bilinear shadow mapping Are standard common filters well known and used. The advantage of these is that they are fast - and therefore possible to use for all lights. Yet they don't somehow seem realistic - especially for area lights, this is where more advanced filters are required. Percentage closer soft shadows & Mip-map penumbrae shadow maps Are advanced filters for area-based shadow maps, and allow for variable penumbra size. Note that the images might have a bit low bias, resulting in small artifacts (I didn't really fine tune anything for taking the images). These shadow filters should allow for most scenarios I can imagine (but technically if any other is required it can be added).

Vilem Otte

Vilem Otte

 

Ludum Dare 40

Since Ludum Dare 35 I'm regularly participating in every one of them and this one wasn't exception. My release thoughts are positive - this time I've again worked with one friend (with whom we've worked also in the past on Ludum Dare), and I enjoyed it a lot. As this is not a post mortem yet, I will not go into details what went right or wrong - meanwhile I'll just show the results and put out few notes... Yes, that's one of the screenshots from the game (without UI). I'm using this one as a "cover" screenshot - so it should also be included here. Anyways this was my another experience with Unity and maybe one of the last Ludum Dare experiences with it. While I do like it, if I can think about suitable game for my own game engine for the theme next time, it's possible that I won't use Unity next time. Ludum Dare Each 4 months or so, this large game jam happens. It's a sort of competition, well... there are no prizes, and I honestly do it just for fun (and to force myself to do some "real" game development from time to time). It takes 48 or 72 hours to create your game (depending on whether you go for compo or jam category), and there are just few basic rules (which you can read on site - https://ldjam.com/). Then for few weeks you play and rate other games, and the more you play, the more people will play and rate your game. While ratings aren't in my opinion that important, you get some feedback through comments. Actually I was wrong about no prizes - you have your game and feedback of other people who participate in Ludum Dare as a prize. Unity... I've used Unity for quite long time - and I have 2 things to complain about this time, majority of all used shaders in Air Pressure (yes, that is the game's name) are actually custom - and I might bump into some of them in post mortem. Unity and custom shaders combination is actually quite a pain, especially compared to my own engine (while it isn't as generic as Unity is - actually my engine is far less complex, and maybe due to that shader editing and workflow is actually a lot more pleasant ... although these are my own subjective feelings, impacted by knowing whole internal structure of my own engine in detail). Second thing is particularly annoying, related to Visual Studio. Unity extension for Visual Studio is broken (although I believe that recent patch that was released during the Ludum Dare fixed it - yet there was no time for update during the work), each time a C# file is created, the project gets broken (Intellisense works weird, Visual Studio reports errors everywhere, etc.), the only work around was to delete the project files (solution and vcxproj) and re-open Visual Studio from Unity (which re-created solution and vcxproj file). Unity! On the other hand, it was good for the task - we finished it using Unity, and it was fun. Apart from Visual Studio struggles, we didn't hit any other problem (and it crashed on us just once - during whole 72 hours for jam - once for both of us). So I'm actually quite looking forward to using it next time for some project. Anyways, I did enjoy it this time a lot, time to get back into work (not really game development related). Oh, and before I forget, here they are - first gameplay video and link to the game on Ludum Dare site: https://ldjam.com/events/ludum-dare/40/air-pressure PS: And yes I've been actually tweeting progress during the jam, which ended up in a feeling, that I've probably surpassed number of Tweets generated by Donald Trump in past 3 days.

Vilem Otte

Vilem Otte

 

Voxel Cone Tracing - Global Illumination

Having a pause from handling files and editor is good, to at least update something in rendering and keep motivation high. So I went ahead and implemented voxel cone tracing global illumination (and reflections of course). Anyways, image time: Although quite dark, secondary shadows are visible. Note, global illumination is full integrated into the editor. Reflective box, global illumination debug buffer (in Debug window), and color bleeding visible from spotlight. Anyways so much for the show - so how is it done? In short: Scene is voxelized, during which phase lights and shadows are injected in. Reflection pass performs cone tracing, the cone angle is defined based on material properties GI pass performs cone tracing for global illumination Lighting pass has 1 fullscreen quad for indirect light (and reflections), and then 1 for each light (which I'd like to replace with tile-based method) Resolution for reflection and GI pass can be any (therefore even sub-sampling can be done), actually in the images Scene is voxelized into 512x512x512 buffer, Reflection & GI pass are done at FullHD with 1x MSAA, and Lighting pass is done with 8x MSAA. Original G-Buffer generation is done at 8x MSAA. Everything is resolved later (actually even after the tone mapping pass). I have an option to switch from voxel texture into sparse voxel octree, yet it is still heavily un-optimized (and slower), although having a lot smaller memory footprint. When I manage to find some more time for that, I'd like to switch over to sparse voxel octree only. If possible, I'd like to re-visit resource management and dynamic re-loading, which would be a bit less 'showcase' and more 'coding' topic. Other than that, virtual shadow maps and virtual textures are going to be visit and attempted by me, hopefully in next weeks. Side note: If you're trying to implement VXGI or voxelization on the GPU, and having some questions - I'll gladly answer them. That should be it for today, thanks for reading!

Vilem Otte

Vilem Otte

 

Dynamic resource reloading

Making editors is a pain. I have a list of thousands of items I'd rather do than this - yet I made myself a promise to drag at least one full featured editor tool over the finish line. There are few reasons for that: I believe I have quite useful engine, it was my pet project all these years, it went through many transformations and stages - and solid tool is something like a goal I'd like to do with it, to make it something better than "just a framework". I'm very patient person, and I believe also hard working one. Throughout the years my goal is to make a game on my own engine (note, I've made games with other engines and I've used my engine for multiple non-game projects so far -> it eventually branched to be a full-featured commercial project in past few years). I've made few attempts but mostly was stopped by lacking such tool - that would allow me to build scenes and levels, in an easy way. And the most important one ... I consider tools one of the hardest part in making any larger project, so it is something like a challenge for me. Anyways so much for motivation, the tool is progressing well - it can be used to assemble scene so far, various entities (like light or materials) can have their properties (components) modified, with full undo/redo system of course. And so the next big part was ahead of me - asset loading and dynamic reloading. So here are the results: Engine editor and texture editor before my work on the texture. And then I worked on the texture: And after I used my highly professional programmer-art skills to modify the texture! All credits for GameDev.net logo go to its author! Yes, it's working. The whole system needs a bit of cleanup - but in short this is how it works: All textures are managed by Manager<Texture> class instance, this one is defined in Editor class There is a thread waiting for change on hard drive with ReadDirectoryChangesW  Upon change in directory (or subdirectories), DirectoryTree class instance is notified. It updates view in bottom left (which is just a directory-file structure for watched directory and subdirectories), and also for modified/new files creates or reloads records in Manager<Texture> class instance (on Editor level) The trick is, reloading the records can only be done while they're not in use (so some clever synchronization needs to be done) I might write out some interesting information or even short article on this. Implementing it was quite a pain, but it's finally done. Now short cleanup - and towards the next one on my editor todo list! Thanks for reading & see you around!

Vilem Otte

Vilem Otte

 

Game Engine Editor

So, I'm slowly progressing with my hobby project. This is the first time I write something about the project directly tied to it, or more likely essential part of it. I'll share few details here about the editor development. Making useful game/engine editor that is easy to control is definitely a long-run task. While the engine itself is updated to support Direct3D 12 currently, there was no real editor that could be at least a bit generic. For my current goal with this project, I decided to start with editor and work from there. So where am I at?   I'm already satisfied with some of the basic tasks - selection system, transformations, editing scenegraph tree (re-assigning object elsewhere - through drag&drop, etc.), undo-redo system (yet with adding more features this needs to grow), I'm definitely not satisfied with way I handle rotations edited through input fields. Component editing is currently work-in-progress (you can see prototype on the screenshot), and definitely needs add/delete buttons. It is not properly connected with undo-redo system yet, but it works. So what are problems with components?   They're not finished, few basic were thrown together for basic scenes, but I'm not satisfied with them (F.e. lighting & shadowing system is going to get overhauled while I do this work) Undo/redo tends to be tricky on them, as each action needs to be reversible (F.e. changing light type means deletion of current Light-deriving class instance and creation of new different Light-deriving class instance) Selecting textures/meshes/..., basically anything from library, requires a library (which has no UI as of now!)   Clearly the component system has advantages and disadvantages. The short term plan is:   Update lighting & shadowing system Add library (that it makes sense!) with textures, meshes and other data - make those drag&drop into components Add a way to copy/paste selection Add a way to add/remove components on entities Add save/load for the scene in editor format   Alright, end of my short pause - time to continue!

Vilem Otte

Vilem Otte

 

Ludum Dare 39 - Release thoughts

Yet another Ludum Dare has come around, and this time I've participated without a real team on development side (I had some help from my sister in audio art, ideas, gameplay balancing and user interface). Before publishing full post mortem I'd like to provide a link: https://ldjam.com/events/ludum-dare/39/im-an-nuclear-engineer Last 3 Ludum Dares really encourage me into starting and finishing some more serious game project, although I'm still thinking about it...

Vilem Otte

Vilem Otte

 

Ludum Dare 38 - Post Mortem

Few weeks ago, I again participated in Ludum Dare and yet again in a team. There were actually three of us and we created the game in 72 hours for 'Compo' category. Some time passed and it is finally time to write a post-mortem. Before going further, I'd like to point everyone out to the game and a walk-through video for it. The game is still available at: https://ldjam.com/events/ludum-dare/38/run-u-fool And you can view the video here: [media] [/media]Let me start with short overview of how it went, for me this was actually already 4th Ludum Dare if I'm not mistaken, and 2nd Ludum Dare in a team. The routine is fairly similar each time, we spend afternoon of the Friday discussing what could be made for each possible theme (as in Europe the time theme is shown is during our night hours), then going for sleep as we start working on the game early in the morning. Originally we have planned a game with small planet or planets, but as we saw multiple people going for the same topic on Twitter, we decided to go different way. The concept originally was based on world getting smaller, while the player's goal was to run away to reach the next level, ultimately reaching another world and saving himself. What went right? Separating tasks between each other, while giving others insight every now and then. It all ended up so well, that everyone of us knew what we're doing and what we're going to do. The idea behind the game was a lot more simple compared to our previous Ludum Dare, where the game we mapped on theme was quite complex. Also having an in-game tutorial this time was definitely a plus. It gave a player perfect idea of how to progress through game, we haven't received any feedback in sense that: players had no idea what to do. Which is definitely a success. Technical execution of the game. I'm quite proud to what we have created technically within those 72 hours, as all art assets - models, textures, shaders & effects were done in that time frame. I even decided to put few selected effects as appendix into this "post mortem" to explain how we did it to other fellow participants. What went wrong? As each competition, time planning. No matter how hard you try - it will always go wrong. While all of us were mostly free on Saturday and Sunday, I was the only one finishing the game on Monday (with help of one another guy, who helped me with some final testing). The rest of the team had to be at work, and I took few hours off, to wrap everything up. Main menu. That part of the game is definitely the one that is the least complete, with just few text-buttons to start levels. Nothing really interesting goes on there, and sadly we had no time to think off and create some art for it. Yet, it serves its purpose and has all the functionality we needed. Build for website (e.g. WebGL). I already knew that browser builds are slow, after all I've worked with browser rendering through WebGL in past as a job. That was about 3 or maybe 4 years back. That was on a custom-built engine (mainly for rendering) which was faster than Unity in comparison ... since then browsers didn't really move a lot.
They are still way too slow to compete with desktop based applications (even the ones running on .NET beat them in order of magnitudes of speed). The solution? Reduced effects, reduced physics, shatters have about 1/10th of objects and it is still several times slower compared to desktop build on the same machine (and I'm talking about testing rigs with Ryzen 1700 + Radeon Rx 480 or Core i7 + GeForce 1070 ... on neither of those it was smooth in WebGL build). Summary Those were few of my basic points. All in all, I really enjoyed Ludum Dare and hopefully I'll participate also in the next one. It may be as a team, or solo. Which is still unknown, as the next one happens during the summer and I fear that some of us might be out for holiday at the time. For those of you, who participated in Ludum Dare or are generally interested in what few effects we put out - with little details and/or description, please read the following sections. For the rest, I hope you enjoyed the game, and feel free to share any thoughts you have about the game (or anything you have in mind). Appendix - Force Field Effect Once we came up with and idea of the game, it was obvious we need some kind of blocking feature in the way. Such item was a force field. The first model I created was a gate to hold force field, as I was against just putting up a plane with some effect. The basic model looks like this: [sharedmedia=gallery:images:8602] Which followed by adding materials, by adding color, metallic and roughness I achieved the final effect for gate that holds the force field: [sharedmedia=gallery:images:8603] Now the final effect, done by simply rolling multiple textures one over another - changing intensity and using some random rolled texture as offset. The final animation is in there, although in my opinion it can't be noticed unless somebody really looks at the force field. The last part was lighting itself, which is done with multiple point lights to simulate the effect of area lighting from the actual force field. The final look: [sharedmedia=gallery:images:8604] And also a video of effect in action: [media] [/media] Appendix - Chain Physics As the development of the game progressed on, we realized, that the player can go through the levels without too much effort. So the idea was to block his progress through the game somehow. I spent some time adding some rigid body objects, but those were not enough to block the player. You can literally go around, or just knock them. Which brought me to the idea, what about some chains that would slow you down. The chains had to react based on the physics of course, and as objects have mass - when you push them it will slow down your speed. But visually they have to behave as 'rope'. At this point there was no time to write some advanced 'rope physics' from scratch with a lot of parameters. So I've settled with simple idea, use few rings in chain as a rigid body and add hinge joints in between the nodes. The result was this: [sharedmedia=gallery:images:8605] And here it is in action, I was surprised how good it looks in movement - so I didn't really touch it anymore. It did its purpose. [media] [/media]Appendix - Shatter Once our idea for the game was settled, I noted that we do need to have some special effect for floor falling apart. The idea behind everything was to use a 'Voronoi shatter' technique to break down the floor. So I've prepared whole floor model and the one with dozens of objects in which floor shatters. [sharedmedia=gallery:images:8606] First idea was to just let it 'explode' downwards, but it didn't look really good. What we did in the end is, that once floor tile was determined to be destroyed, the whole object got disabled, while shatter one was enabled. Each single voronoi cell had rigid body assigned, but without gravity applied and kinematic flag set to true. Along with that we added a random timeout at which the cell gravity was re-enabled and stopped being kinematic only. By cleverly playing with parameters we ended up with acceptable animation looking like this: [media] [/media]Changelog 5/18/2017 - Initial version published

Vilem Otte

Vilem Otte

 

Ludum Dare - Post Mortem

If only making games was as easy as making custom PCBs This Ludum Dare was a really interesting experience for both of us. We wanted to do LD36 already together, but one of us couldn't, so we had to wait for a LD37.


You can grab the game at: http://ludumdare.com/compo/ludum-dare-37/?action=preview&uid=8633
Theme When planning a evening before, we really hoped for a "Small World" theme and were able to come up with an interesting game in matter of minutes. The other themes seemed lackluster, because they were often too limiting - and One Room was one of them. When we woke up at Saturday morning and started discussing it, we didn't know for like 3 hours what kind of game to make. There were several options (some of which actually appeared in LD as games - solving murder in one room, time traveling in one room, cleaning a room, ...), but we couldn't think of anything that doesn't need a good storyline. We just knew two things for sure - we DEFINITELY didn't want to do a room escape game, and we wanted fancy 3D graphics. After joking about stupid ideas, we somehow came to conclusion that conveyor belt game located in one factory hall could be quite nice and went to discuss details more and decided on our PCB factory game. The development The entire game was developed by two people -- one was focusing entirely on programming the game mechanics and logic and other one was doing mostly graphics, lightning and other visual things (he implemented Inverse Kinematics for a robotic hand from ground up!).

I guess it's angry and searching for something inside those crates?!
-- Pavel Kou?il (@Pajka) December 10, 2016
In the first day, we managed to implemented conveyor belts, robotic hands and basic movable objects for the transporters. This was a good prototype, and the game already felt like a fun! So we knew we were on the right track.
The second day was dedicated to modeling rest of game objects and programming the rest of the game. At the end of these two days (around 4 am) we had a almost finished game. It still needed some polishing and changes, but it was playable from the beginning to the end.

This is what we managed to do over the weekend. Shame we have to work today, so there will be only small changes, (if any).
-- Pavel Kou?il (@Pajka) December 12, 2016
To make the game totally complete, we really needed that third day - but we were all busy with our real lifes, meaning we couldn't make much progress, apart from just a few bugfixes and polishing fixes. But we think we still managed to do quite a nice game over the course of these 3 days!
The Good We really had fun when developing our One Room Factory and shared many laughs over TeamSpeak when developing the game - one can only wonder if the game would be even better if we didn't have to work remotely, but could develop in one room together as some other teams did.
Also, since this was a first LD for one of our members (and first released game too!), we definitely learned quite a lot. Not only from the actual LD, but also from the feedback we've gotten. It was definitely an enjoyable and fun weekend! The Bad The HW requirements are quite demanding. Also, based on the comments so far, the game is quite difficult to understand; we did a basic tutorial, but because of time constraints (having to work on Monday AND being based in Central Europe cuts a lot of needed time due to timezone), we just didn't have time for a proper tutorial.
Combine this with the complexity of the game (and players not being familiar to conveyor games), this game seems to hard to understand for some people. So, to fix this, we are releasing a video tutorial for people who would still want to play the game!
(Don't forget to turn on annotations)
The Future Depending on how we do in the LD, we are considering to finish the game and release it - we are reading the feedback people give us in comments and it makes us really happy when people like the game! The most rewarding comments are from people who want to see the finished version (with a proper tutorial) - and it's definitely possible it will happen! ;)
If you decide to check our game out, we will be happy for any feedback you will give us! If not, we are at least thankful you decided to read our post.
To try our game, you can do so here:
http://ludumdare.com/compo/ludum-dare-37/?action=preview&uid=86334
Vilem Otte & Pavel Kou?il

Vilem Otte

Vilem Otte

 

Compilers articles

It's me, bugging around again. I finally got back to work on my compilers-related series, while it is focused on people untouched by automatons, VMs and compilers in general (or more likely the ones that are using it, while not knowing what goes inside), I tried to keep code-base quite small (and still manageable and readable by average viewer). The funny thing around this is, that I've never actually worked on any big compiler (like GCC or such). So the series might give a look of someone who tried to understand how it works and how to do it, with formal languages and automatons theory in head (or at least whatever stayed there after university). While doing the compiler by myself as a form of education and challenge, I realized how little of this is covered in general - and that a lot of people don't fully understand what goes on behind the scenes - which motivated me to learn about compilers and languages more in theory and write at least a few-article series to introduce others into this problematic (and possibly help them avoid a lot of troubles I've hit while making my first compiler). Actually the original final version of the compiler (even before I started working on articles) was able to compile something like this:int test(int* x){ x[0] = *x * 2; return -1;}int mult(int** x){ int* y = x[0]; y[0] = x[0][0] * 5; return -1;}int main(){ int z[3] = {1, 2, 3}; int x = 7; int y = test(&x); y = mult(&z); while (x > 0) { y = y * 2; x = x - 1; } return z[0];}
Into an assembly like this:.data.texttest: mov eax, 0 push eax mov ebx, [ebp+0] mov eax, ebx mov eax, [eax] push eax mov eax, 2 pop ebx mul eax, ebx pop ebx mov edx, 4 mul ebx, edx mov edx, [ebp+0] mov [ebx+edx], eax mov eax, 1 neg eax pop ebx push eax mov eax, 1 add ebx, eax mov eip, ebxmult: mov eax, 0 mov ebx, 4 mul eax, ebx mov ebx, [ebp+0] mov eax, [eax+ebx] mov [ebp+8], eax add esp, 4 mov eax, 0 push eax mov eax, 0 mov ebx, 4 mul eax, ebx mov ebx, [ebp+0] mov eax, [eax+ebx] mov ebx, eax mov eax, 0 mul eax, 4 mov eax, [ebx+eax] push eax mov eax, 5 pop ebx mul eax, ebx pop ebx mov edx, 4 mul ebx, edx mov edx, [ebp+8] mov [ebx+edx], eax mov eax, 1 neg eax sub esp, 4 pop ebx push eax mov eax, 1 add ebx, eax mov eip, ebxmain: mov eax, esp add eax, 4 mov [ebp+4], eax add esp, 4 mov eax, 1 push eax mov eax, 2 push eax mov eax, 3 push eax mov eax, 7 mov [ebp+20], eax add esp, 4 push ebp mov ecx, esp mov ebx, [ebp+20] mov eax, ebx mov eax, edx push eax mov ebp, ecx push eip call test pop eax sub esp, 4 mov esp, ebp pop ebp mov [ebp+24], eax add esp, 4 push ebp mov ecx, esp mov ebx, [ebp+4] mov eax, ebx mov eax, edx push eax mov ebp, ecx push eip call mult pop eax sub esp, 4 mov esp, ebp pop ebp mov [ebp+24], eaxL0: mov ebx, [ebp+20] mov eax, ebx push eax mov eax, 0 pop ebx sub eax, ebx neg eaxjle L1 mov ebx, [ebp+24] mov eax, ebx push eax mov eax, 2 pop ebx mul eax, ebx mov [ebp+24], eax mov ebx, [ebp+20] mov eax, ebx push eax mov eax, 1 pop ebx sub eax, ebx neg eax mov [ebp+20], eax jmp L0L1: mov eax, 0 mov ebx, 4 mul eax, ebx mov ebx, [ebp+4] mov eax, [eax+ebx] sub esp, 24 pop ebx push eax mov eax, 1 add ebx, eax mov eip, ebx__start: push ebp mov ebp, esp push eip call main pop eax sub esp, 0 mov esp, ebp pop ebp
While it was structured as procedural-based compilers it wasn't good enough to be presented. So for article purposes I tried to re-work everything in better structure from scratch. Not covering just practical implementation, but hitting a theory from time to time. At that point I realized what all I want to put in the article(s):
Arithmetic math operations on integers
Arithmetic math on pointers and their reference/dereference operators
Variables and arrays
Standard control constructs (if, do-while, while, for)
Function calling (e.g. define and use a call convention)
Interaction with host application (the one running VM)
More types - shorts, bytes and floats (and of course variable promotion)

So far, the articles are still somewhere in the middle, slowly getting finished. Although I'm already planning what is going to be next, but let's keep that for next entry.

Vilem Otte

Vilem Otte

 

Dead... or?

I noticed on myself that my activity on forums dropped to almost zero over the last two years, it was quite high before that (gradually dropping while I was working on my diploma), well... I finished it, and started doing more business and less game development, less (non-job related) graphics development, etc. So I slowly got to the stage where I noticed that I'm starting to be separated from game development, which lead me to a point where I realized I don't want to live a life without making games. Since that point I did two entries to last 2 Ludum Dare competitions ... even though I love making tools, fine tuning my game engine source (which I have actually never used for game), etc. ... I used Unity for that. Anyways - a game is a game. Nevertheless, to keep myself connected to game development I decided to start doing a journal here, regarding my development ideas, thoughts, post mortem articles and maybe even some progress from time to time. And most importantly, code and articles!

Vilem Otte

Vilem Otte

Sign in to follow this  
  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!