triplanar3.cgfx |
triplanar3.shader |
Cross-Origin-Embedder-Policy="require-corp" Cross-Origin-Opener-Policy="same-origin"
Its been a while since I posted a shader, so here's one that i did a while back for design purposes. Its a Triplanar shader that does not require a texture - the checker pattern is the result of a little shader math. This is great for layout purposes, as you can simply stretch and skew an object all you want, and still get an accurate read on how it sizes up on other objects in the level. The shader is Unlit and is quite simple compared the the other Triplanar shaders I wrote, since it is used for prototyping more than anything. It does use blending, although you could try cutoff blending like in Triplanar 2.
0 Comments
I actually first attempted this years ago when i was first learning shaders, but looking back on it i missed out on a few details. Today I know a bit more and can read the equations a bit better, so i think that this is pretty close. I downloaded the mesh and textures for the heavy, which made things a lot more clear as well. There were definitely some things to be learned from this, namely how important specular and rim lighting are for the TF2 look. They use 2 phong highlights both of which are modulated by a rim falloff, in addition to constant rim lighting. They also use a specular exponent map, which adjusts the power of the specularity on a per pixel level. This causes very broad highlights that are also varied across the surface and gives it a very distinctive look. Hopefully I'll be able to use what i learned on some of my other shaders. Maya CGFX File:
Unity SHADER File:
This is one I just made up - not sure if anyone else is doing something similar, although i wouldn't be surprised since its pretty simple. Basically I use a texture to warp the lighting value prior to sampling the light ramp. This causes a bit of disruption to the normally perfectly clean transitions between light values. Depending on the style you are going for this might be beneficial. Here is regular Ramped lighting and Warped Ramp Lighting Depending on your texture you can achieve a variety of effects: Maya CGFX File:
Unity SHADER File:
I figured i would give crosshatching a shot, since it seems to be semi important. A lot of games with a non-photo-real art style use this to varying degrees but it is often done directly on the texture. This means that it will not actually react to the light at all. This paper presented a technique that is reactive to light, by using varying amounts of hatching and blending between them. For simplicity's sake i only used 3 textures instead of 6, since i hatching on light areas is confusing. Instead I just use a pure white for the brightest 2 areas and begin hatching in the midtones, and use the heaviest hatching on the darkest 2 areas, where its hard to notice the difference. This allows me to cram everything into the RGB channels of a single texture before splitting them out and recombining them in the shader. I used the blending technique found here to calculate the hatching level. It also includes a offset and intensity modifier to let you control the shadow level where hatching starts and the intensity of the hatching respectively. Here it is pushed back into only the deepest shadows with the original hatching texture and an alternate I made (I found you could use it for more types of tone mapping than hatching). I like this because i think it helps the shapes in the deeper shadows read a bit better. I did have to rip out multiple lights from the shader to keep it the same between Maya and Unity, as there is no way to make this work that i know of with multiple lights in Unity. I may try some fakery later to get them back, but i wasn't totally happy with them anyway - Ramped lighting doesn't seem to blend additively very well, but that may just be my ramps. Maya CGFX File:
Unity SHADER File:
Minor update with 100% more Fresnel Outlines! In order to allow thick outlines, I added an intensity modifier which allows me to skip powering entirely. Since we need sharp outlines, I just floor the resulting value and let the intensity modifiers do the work adjusting the thickness instead. Like the geometry outlines, it can also be thickened according to the light. Here it is with the geometry outline in blue and Fresnel outlines in red I'm still not completely satisfied with these. Because of Unity's light system it costs 4 passes when it should rightfully only cost 2 like it does in Maya . Additionally it misses a lot of interior lines that a more robust solution might be able to catch. In any case, here it is. If i improve it in the future i'll post that as well. Maya CGFX File:
Unity SHADER File:
This is a fairly simple extension of my ramp shader. It uses an early pass to inflate the vertices via the vertex normal and color them. This produces an outline that is rendered in world space and fades appropriately in the distance. Potentially you could model your own outline mesh and get similar results but this is somewhat impractical for building a whole game, so its worth knowing how to do. You can also shift the outline according to the light direction, giving you thicker outlines in the shadows. I plan to explore a other methods to generate other lines, as this is only really useful for thin contour lines. Frensel lines should be able to enhance these, bridging gaps and thickening them up. Maya CGFX File:
Unity SHADER File:
Ramp shading is another option that compares well with basic Cell shading. By using a ramp to decide the color and intensity of the light, we can gain more control of the light-dark transition. Here is Cell Shading on the Left and Ramp Shading on the ramp with an equivalent ramp texture. The main benefit we can immediately notice is the softening of transitions. Cell shading can introduce aliasing and ramp shading smooths this somewhat. Since the lighting is now governed by a ramp, we can easily control the brightness and falloff of the lighting. It also allows us to easily create various effects as seen below: Maya CGFX File:
Unity SHADER File:
Decided to take a break from realistic shaders and go in the opposite direction. Illustrative techniques such as this one produce have good results but a lot depends on the style you are going for.This shader supports banded phong lighting, giving you a cell shaded effect. Below you can see regular Phong and Cell Shading It also supports normal maps as usual. I had to remove the rim lighting for now, although i may find a way to re-implement it in the future. The specularity is sharpened to match the style and Lerped into the diffuse as I found that reads better for this style. A small amount of specularity is added into the diffuse on a constant basis, as i found it looks nice in the past. Maya CGFX File:
Unity SHADER File:
In my searches for the more advanced hair shading techniques, i stumbled upon the full paper that discussed the sorting technique from before. It cleared a few things up, but also demonstrated a Kajiya-Kay specular lighting technique with a few ideas taken from Marschner. So I stole it (for learning of course). Here is my previous implementation on the left and Kajiya Kay on the right. The differences are subtle, mostly manifesting in a differently colored highlight and the ability to shift the highlight along the hair. EDIT: I finally came back and figured out Ambient Occlusion with transparency. It improves hair layering quite a bit. Before and After: In order to generate Ambient Occlusion with an alpha map, you have to follow a slightly hacky procedure: First assign your texture with alpha to a lambert material and unlink all lights in your scene. Next, add some shadow casting ambient lights. Cast shadows are off by default, so you will need to check the options. I used 4 to surround the hair from above, but you will probably need more for objects like trees. Here is my baking setup, ugly alpha sorting and all: Next, go to the texture bake set and set it to bake 'Light Only' - higher samples generally looks better, everything else is pretty irrelevant. Finally, open the Mental Ray Baking Options and and ensure bake shadows is checked before baking your lighting. And you are done. Oddly enough this tends to bake faster than true Occlusion, even on high sample amounts. WTF Maya? I've updated both these and my previous anisotropic hair shaders to handle ambient occlusion. I use the second UV set for Ambient Occlusion Maya CGFX File:
Unity Shader File:
I started this as a fix for Unity's transparency sorting issues and it kind of snowballed from there. Transparency sorting is a somewhat tricky issue, and this becomes abundantly clear when trying to create hair. I was wanting to try out a few hair shaders since i just finished Anisotropic highlights, but the sorting issues were driving me up the wall. Fortunately i found this paper which presented a solution. Basically it is a multipass shader that renders a depth mask first, and then renders the regular lighting with some adjusted depth settings. By rendering a depth mask early, they are able to avoid using alpha clip ever again. This results in properly sorted double faced transparency (I really should try this on some of my other shaders). Here is some hair i made real quick on a female head i got from turbosquid. Its not the greatest but hey that's why i'm not a character artist. On the Left we have Unity's transparent specular. You can see the z fighting all along the left side. On the right we have my shader, with corrected sorting. I liked the results so much it forced me to try out multipass in maya and i got it working in there too. I included an unlit version in that one, in case the anisotropic lighting slows things down too much while modeling. The transparency is a little ugly because of how Maya mips textures in the viewport for cg shaders but it works. I did handle a few things slightly differently. Rather than setting the alpha clip threshold to 1, I set it to 0.3 and expose a power attribute to adjust the amount of blending/clipping. Additionally, in the paper they mentioned turning the depth writing back on during the final pass, but this caused some areas to disappear so i turned it off again ¯\_(ツ)_/¯ EDIT: I updated the shaders to include Ambient Occlusion. For more infomation about this and how to bake AO with an alpha, check out my Kajiya Kay implementation. Maya CGFX File:
Unity SHADER File:
I actually started this last week but got sidetracked because i wasn't satisfied with the implementation i came up with. Most resources i found do not respect normal maps when it comes to calculating anisotropic reflection which i wasn't particularly a fan of. Anisotropic highlights use tangent and binormals to calculate the specular highlight, and these are generally ignored when it comes to normal mapping (they generally aren't relevant to the lighting). In order to fix this I attempt to calculate per pixel tangent and binormal vectors based a cross product with the normal map. This is a little more expensive and may turn out to be inaccurate but i think it looks a bit better than vertex normals. But hey i'm just an artist who likes to hack things, what the hell do i know about doing things properly? Vertex normals on the left, per pixel normals on the right. Another minor issue was the use of a few square roots, exponents, divisions and multiplications. After the dot products being divided by the anisotropic controls, everything else seemed to be modifying intensity, but having very little visible effect. I'm not really a fan of extra calculations, so i replaced it with a division by pi which seemed to have the roughly the same effect. This misses out on a bit of rimlight hotness though, so i approximated that with (pow( (1-ndotV), 3) * 3) * specTotal and added that to the specular total prior to the pi division. The multiplication by 3 is arbitrary and it might be possible to find a better value but this looks close enough to me. You can see my approximation below on the left using vertex normals, compared to true Ward specularity on the right, with specular multipliers of 1, 4, and 8 respectively. Overall i'm pretty happy with it. It's kinda cool to be able to fake these more advanced shaders with cheaper implementations. I'll have to re-look at Cook Toorance and Strauss at some point to see if I can gain anything with similar fakes. Of course if anyone can find a reason why these ideas wouldn't work i'm open to suggestions. Maya CGFX File:
Unity SHADER File:
A lot of these physically based models have papers which draw comparisons to another shading model called Cook Torrance. Cook Torrance has quite a few features which make it an attractive physical model: it features a roughness term like Oren-Nayar, and a frensel term that modifies the specular highlight based on viewing angle. Additionally, it calculates self shadowing from micro facets similarly to Strauss. It seems very similar to Strauss actually, and incorporates a lot of the same concepts. For my implementation I used Beckmann Distribution to calculate roughness and Schlick's approximation for the Frensel. One of the most interesting things is it's use of the frensel, which directly contributes to the diffuse and specular highlights. When the frensel reflection an roughness are set to 1 , it looks a lot like the frensel i've been adding to the diffuse for a while, albeit modeled much more nicely. Cook Toorance on the left, modified phong on the right. I'm just going to go ahead and pretend that it wasn't a complete hack and was exactly what i was going for in the first place ^_^ Maya CGFX File:
Unity SHADER File:
This is a shader technique used for approximating specular highlights. Most phong derivatives use a specular power and the power function is one of the more expensive operations in shaders. That's why Christophe Schlick proposed a way to approximate this effect in his paper. He goes into a few other approximations that can be made which are honestly pretty slick, but I'm focusing on the specular here. Here we have Schlick's specular on the left and phong on the right both with a "specular power" of 40. As you can see the highlight spreads out somewhat but the core follows essentially the same shape as with phong. Some might even find this preferable to phong, as perfectly sharp highlights can seem artificial. The cool thing is this technique could be applied to other things that use power as well, which could save you some instructions to spend elsewhere. For example, this should work equally well for a blinn shader. Maya CGFX File:
Unity SHADER File:
Oren Nayar got me more interested in physically based shaders. In the simplest sense, they are shaders that approach lighting from a Physics rather than mathematical perspective. Oren Nayar does this by adding a roughness attribute that attempts to simulate the effects of microscopic bumps on light reflection. While these bumps would be smaller than a pixel, their effect is quite noticeable in terms of overall light contribution, and so they are worth taking into account. Strauss is another shading technique that is physically based. I based my implementation on this post. The interesting thing about Strauss is that it only uses 3 attributes to calculate diffuse and specular light: how smooth the surface is , how metallic it is, and how transparent it is. This greatly simplifies the material setup and makes the lighting more consistent than phong, producing a wider range of materials and generally producing more realistic results. Consider the following scenario, with Strauss on the left and Phong on the right. As you can see the light is causing the phong to blow out in areas, and is generally more plastic in appearance. While we could adjust the Phong to work better, this might cause it to look worse in another lighting situation. For example, a common hack would be to tint the diffuse with a darker color, say 50% grey. This would keep your highlights from blowing out but it would also make your shadows twice as dark. This leads to more hacks and tweaks, which eventually means that all the materials are carefully tweaked to work for certain scenarios, but perform worse in others. Instead, by taking a few more things into account in the shader, we can get more consistent results with less work - always a good thing. 1 thing to note is that i haven't figured out what to do with my spec map yet. It seems rather silly to use for this shader but i haven't got a better solution just yet so it stays as it is for now. It will probably become either a metallic or roughness map in the future once i learn a bit more about physically based rendering, but i'm not sure what makes the most sense yet. Maya CGFX File:
Unity Shader File:
After getting Oren-Nayar working, I started looking for quicker solutions, as I can't justify moving to SM 3.0 just for diffuse lighting. I happened upon Pope Kim's blog post about an optimized Oren-Nayar shader they implemented for Warhammer 40000: Space Marine. It provided a neat solution that got me back to SM 2.0 (YAY!) but it wasn't quite as good of a match to true Oren-Nayar as i had hoped. Specifically the view dependent Intensity was dimmed somewhat. After a few hours of testing i was able to come up with a solution that brought the intensity back up to normal when the view and light directions were roughly aligned. I found that in this case, if the soft rim light was multiplied by 0 instead of the "fakey_magic" value, the light intensity was corrected. In cases where the light and view vectors were perpendicular, the "fakey_magic" value held true. So in my final implementation I simply multiplied his "fakey_magic" value by a "fakeyFix" value calculated as: 1-(saturate(dot(L,V)) * result ). By setting "fakeyFix" to 1 minus the saturated dot of the light and view vector, I assured the value would be 0 when perfectly aligned and 1 when perpendicular. By multiplying by "result" aka dot(NormalDir, LightDir), we reduce any shadow artifacts due to this method (when rotating towards parallel, the brightness intensifies much too rapidly. This brings it back to normal ranges). Below you can see some comparisons from different views. 1 is full Oren-Nayar, 2 is Pope Kim's approximation, and 3 is my derivative approximation. So far I haven't seen any issues with this approach, but it is a hack on top of a hack on top of what is probably another hack, so uh...buyer beware. In any case I'm gonna call it good for now. All the excitement of exploring diffuse shading has me a bit overwhelmed. Maya CGFX File:
Unity SHADER File:
Oren-Nayar is another alternative diffuse model. It takes the roughness of the surface into account, making it quite suitable for brick, concrete, sand, or numerous other materials. By parameterizing the roughness , we can have a suitable model for everything from a perfectly smooth surface (like lambert) to a rough surface like concrete or chalk. Below you can see Oren-Nayar with 100% roughness on the left and Lambert on the right. Rougher surfaces have a slower falloff which Oren-Nayar takes into account. If we were to go back to 0 roughness it would regain the hotspot like lambert, simulating a smooth surface. Here it is applied to the brick i use way too often to test things. As you can see, Oren-Nayar gives us the matte look we would expect from brick. Unfortunately it is somewhat expensive (pushed me into SM 3.0 with no spec...Yikes!) so i'll be looking for cheaper alternatives. Maya CGFX File:
Unity SHADER File:
Minnaert is a modification of Lambert that works by attenuating the intensity by the view angle and light direction. This means that the surface will be brightest when the light and view directions are parallel and areas facing away from the light will become darker more quickly when compared to Lambert, although the darkest point will never become darker than an equivalent Lambert surface. This is useful for things like cloth. Velvet is a common use case and the results are pretty convincing. Here are 2 shaders with essentially the same settings: 100% Red, no Rim light, no Specular, using a water normal map. On the Left is Minnaert with a darkening factor of 4, while on the right is Lambert. You could get equivalent effects to Lambert by simply setting the darkening factor to 0. It is also a shader that I find benefits a lot from a Rim Light, although it's not so physically accurate (It's can't be attenuated like real lights and so will appear brighter than your real lights. Therefore its a good idea to use lower brightness values). Here is the same shader with a 50% intensity rim light on the left and with no rim light on the right. For ease of demonstration, I stripped specularity completely. You can add whatever specular model you wish back in if you need it. Maya CGFX File:
Unity SHADER File:
Since i first started working with specularity in CG, I have been using the Phong model. I like Phong. It's simple, holds up well at grazing angles, and has generally predictable effects. It does have some known disadvantages though, one of which is the shape of the specular falloff at high angles of incidence. Basically phong remains blobby where we would want it to stretch out toward the viewer. Blinn-Phong addresses this by constructing a halfway vector between light and view direction and retrieving the dot product of the halfway vector and the normals, in place of Phong's dot product of the reflection vector and light vector. This handy illustration shows the effect, with Blinn-Phong specularity on the left and Phong specularity on the right. It does come with a few of its own issues, notably at grazing angles where is seems to form a singularity rather than going stretchy like Phong. This might be worth investigating further in the future. Maya CGFX File:
Unity SHADER File:
This shader combines a few of the concepts from before to create a basic holographic effect. I use single sample world uvs to keep the blend texture from being distorted by vertex deformation and blend the vertices visibility in sync with the deformation. This produces a nice blending effect which could be modified to produce force-fields, shields, or whatever other sci-fi magic you might need. Here are some shots of it in multiple states of blending. It can blend in any direction and has separate controls for the alpha and vertex blending falloff. Here is a video i made way back in the day of an early prototype, before i ported it to CG. Maya CGFX File:
Unity SHADER File:
An alternative triplanar technique that produces sharper masks and does not require 3 texture samples to work if you don't need them. This makes it cheaper if you only need to tile 1 texture, since the uvs will be constructed correctly prior to sampling (no need to mask things off and sample another texture to cover up bad projections). I left the textures the way they were though for demo purposes. Sampling a single texture would just be a matter of removing the masking and sampling single textures with the constructed uvs. Maya CGFX File:
Unity SHADER File:
A little trick i did a while back. Triplanar mapping gives you a clean UV layout based on the world position of the vertex/pixel being rendered. This is very useful when greyboxing a level, as you don't need to be concerned with textures stretching - just scale and move boxes to your heart's content. I decided to add 2 alternative textures to demonstrate how you could use different textures for different projection directions. If you want to blend specular color, power, or whatever with the mask check out some of my tutorials on directional / RGB masking from before - im feeling a bit too lazy today to include it. The blending is pretty good between multiple textures and when applied to curved objects. I have another technique that can be cheaper, but this is the most complete solution i know. Just for fun i reused my moss and dirt textures to make an HD minecraft block. Maya CGFX File:
Unity SHADER File:
This one is a little different than the last vertex animated shader. It is based on Crytek's implementation of plant animation as seen here. It gave me a nice opportunity to try out triangle waves, which are cheaper than sine waves and look better in this case (personally i find sin waves make the plants look like they are underwater while traingle waves look a bit more "snappy"). The shader animation is based on a passed in wind direction in world space, and can be controlled with vertex colors. It is a bit touchy and varies a bit more than i would like due to scale differences between maya and unity, although these could be avoided with a strict control of import scaling to unity. Otherwise it changes the usable strength and size ranges a bit. Maya CGFX File
Unity SHADER File
I took my regular panning uv water and added vertex animation to it. There are some special considerations to take into account when doing vertex animation, namely that your vertex normals will no longer be valid. Because of this, I rebuild the normals inside the vertex shader before passing them along. I use 2 adjacent points to each vertex along the tangent and binormal of the vertex to recalculate the normals. I'm sure there is something more efficient to do this with but it works for now. The animation itself is kinda cool. The main animation creates wave crests with 3 sums of a Fourier series. While this gives decent movement, I felt the peaks all looked too soft and there was no back and forth movement. After watching this video, I realized that good looking wave peaks couldn't be achieved with pure sine waves. In order to achieve this, I would need to push all the verts toward their peaks like in the animation. After a few hours of struggle, I realized that I could get this movement with a cosine function, running on the same frequency as the sine function. Basically the cosine has a similar wave shape as the sine, but reaches its peak and trough a slightly different time than the sine wave. By modifying the vert position in the parallel direction as the wave is travelling, we can push all the verts toward the peaks (for a sine wave traveling down z, add to the z direction according to the cosine wave). As an added bonus, an object modified with this function would exhibit the same bobbing and back and forth motion as a boat would in real life when a wave hits it, without doing any nasty collision detection. So you could actually make "floating" objects that behave naturally with very little effort :) Maya CGFX Shader:
Unity SHADER File:
Edit: Did a little research and found out there is actually a formula that does exactly this called Gerstner Waves. MFW I discover my idea has already been done and is so well known it predates computer graphics:
This translucent technique is designed for objects that are thicker than leaves or paper. It is based on this technique as described in DICE's 2011 GDC demo and it shares a lot of the same techniques as the Crytek translucent technique. In fact, the only major difference that I gathered was that they implemented a normal distortion factor, allowing them to bend the translucency. Other than that it is essentially the same shader code wise. They did mention making use of an inverted Ambient Occlusion map, baked with reversed normals to calculate a thickness map, which is used in place of the transmission alpha in the flat translucency. To test this out, I baked out a quick Inverted AO map of the rhino that comes with zbrush. I chose it because it had a good amount of variance between thick and thin areas. The results aren't bad - thin areas like the ears and horn are translucent while thick areas like his belly are not. I would recommend baking with 3ds Max as it seemed the easiest to get to work correctly with inverted normals. This page has some good info on setting up the bakes. Maya CGFX File:
Unity SHADER File:
After a bit of experimenting in Unity, I found that while Z test is impossible to run in alpha blending, you can get away with it in Alpha Clip. This opens up a few nice possibilities, namely that you could turn backface culling off with alpha clip without sorting issues. This means you can have a 2 sided material for about the same cost as a one sided material - 2 passes as normal, instead of 4 passes to cover base and fill lights for 2 sides. I flip the normals on the backfaces so that normals are accounted for correctly. This works OK but you may run into weird shadowing on high angles of incidence. For now i'm rolling with it, but I am open to any other suggestions :) Thanks to this, I thought it would be a good time to try out flat translucency. It's basically a super cheap subsurface scattering approximation. This is useful for leaves or other paper thin materials that allow some light to pass through. I based my implementation on Crytek's implementation of light transmission, featured in this paper. My implementation is a bit simpler to save a few instructions, but the idea is basically the same. Just skip past the vertex animation for now, I'll get to that later :) Maya CGFX File:
Unity SHADER File:
|
CG ShadersShaders I've built as i teach myself CG. Feel free to download and use for whatever. If you like them you can buy me a beer or something. Archives
December 2014
Categories |