unity怎样让透明的unity 物体加阴影投射阴影

努力加载中,稍等...
暂无新消息
努力加载中,稍等...
已无更多消息...
这些人最近关注了你
努力加载中,稍等...
已无更多消息
努力加载中,稍等...
已无更多消息
Unity 渲染教程(十二):半透明材质的阴影
Rendering 12 Semitransparent Shadows
征集热心朋友翻译文章,奖励规则:每100汉字奖励10QB,30天内互动奖励0 - 50QB.
翻译请求:专家推荐
该文章来自用户转载
Support cutout shadows.Use dithering.Approximate semitransparent shadow.Toggle between semitransparent and cutout shadows.This is part 12 of a tutorial series about rendering. In the , we made it possible to render semitransparent surfaces, but we didn't cover their shadows yet. Now we'll take care of that.This tutorial was made with Unity 5.5.0f3.When objects fade, so do their shadows.Cutout ShadowsCurrently, the shadows of our transparent materials are always cast as if they were solid, because that's what our shader assumes. As a result, the shadows might appear very strange, until you realize that you're seeing the shadows of a solid object. In the case of directional shadows, this can also lead to invisible geometry blocking shadows. Opaque and cutout rendering mode, same directional shadows.In the case of spotlight or point light shadows, you'll simply get a solid shadow.Solid spotlight shadow.Refactoring My ShadowsIn order to take transparency into account, we have to access the alpha value in the shadow caster shader pass. This means that we'll need to sample the albedo texture. However, this is not needed when using the opaque rendering mode. So we're going to need multiple shader variants for our shadows.Right now we have two versions of our shadow programs. One version for cube shadow maps, which is required for point lights, and one for the other light types. Now we need to mix in even more variants. To make this easier, we're going to rewrite our My Shadow include file. We'll use interpolators for all variants, and create a single vertex and fragment program.First, move the definition of Interpolators out of the conditional block. Then make the light vector conditional instead.struct VertexData { float4 position : POSITION; float3 normal : NORMAL;};struct Interpolators {float4 position : SV_POSITION;#if defined(SHADOWS_CUBE)float3 lightVec : TEXCOORD0;#endif};Next, write a new vertex program, which contains copies of the two different versions. The non-cube code has to be slightly adjusted to work with the new Interpolators output.Interpolators MyShadowVertexProgram (VertexData v) {Interpolators#if defined(SHADOWS_CUBE)i.position = UnityObjectToClipPos(v.position);i.lightVec =(unity_ObjectToWorld, v.position).xyz - _LightPositionRange.xyz;#elsei.position = UnityClipSpaceShadowCasterPos(v.position.xyz, v.normal);i.position = UnityApplyLinearShadowBias(i.position);#endifreturn}Do the same for the fragment program. Then get rid of the old conditional programs.float4 MyShadowFragmentProgram (Interpolators i) : SV_TARGET {#if defined(SHADOWS_CUBE)float depth = (i.lightVec) + unity_LightShadowBias.x;depth *= _LightPositionRange.w;return UnityEncodeCubeShadowDepth(depth);#elsereturn 0;#endif}
Clipping Shadow FragmentsWe'll take care of cutout shadows first. We cut holes in the shadows by discarding fragments, like we do for the Coutout rendering mode in the other rendering passes. For this we need the material's tint, albedo texture, and alpha cutoff settings. Add variables for them to the top of My Shadows.#include "UnityCG.cginc"
float4 _Tsampler2D _MainTfloat4 _MainTex_ST;float _AlphaCSo we have to sample the albedo texture when we're using Cutout rendering mode. Actually, we must only do this when we're not using the albedo's alpha value to determine smoothness. When these conditions are met, we have to pass the UV coordinates to the fragment program. We'll define SHADOWS_NEED_UV as 1 when these conditions are met. This way, we can conveniently use #if SHADOWS_NEED_UV.#include "UnityCG.cginc"
#if defined(_RENDERING_CUTOUT) && !defined(_SMOOTHNESS_ALBEDO)#define SHADOWS_NEED_UV 1#endifAdd the UV coordinates to the vertex input data. We don't need to make that conditional. Then conditionally add the UV to the interpolators.struct VertexData { float4 position : POSITION; float3 normal : NORMAL;float2 uv : TEXCOORD0;}; struct Interpolators { float4 position : SV_POSITION;#if SHADOWS_NEED_UVfloat2 uv : TEXCOORD0;#endif#if defined(SHADOWS_CUBE)
float3 lightVec :TEXCOORD1; #endif };Pass the UV coordinates on to the the interpolators in the vertex program, when needed.Interpolators MyShadowVertexProgram (VertexData v) { …#if SHADOWS_NEED_UVi.uv = TRANSFORM_TEX(v.uv, _MainTex);#endifreturn}Copy the GetAlpha method from My Lighting to My Shadows. Here, whether the texture is sampled has to depend on SHADOWS_NEED_UV. So check for that instead of whether _SMOOTHNESS_ALBEDO is defined. I marked the difference.float GetAlpha (Interpolators i) { float alpha = _Tint.a; #ifSHADOWS_NEED_UV alpha *= (_MainTex, i.uv.xy).a; #endif
return}Now we can retrieve the alpha value in the fragment program, and use it to clip when in Cutout rendering mode.float4 MyShadowFragmentProgram (Interpolators i) : SV_TARGET {float alpha = GetAlpha(i);#if defined(_RENDERING_CUTOUT)(alpha - _AlphaCutoff);#endif…}To make this actually work, add shader features for _RENDERING_CUTOUT and _SMOOTHNESS_ALBEDO to the shadow caster pass of My First Lighting Shader.
Pass { Tags { "LightMode" = "ShadowCaster" } CGPROGRAM #pragma target 3.0
#pragma shader_feature _RENDERING_CUTOUT#pragma shader_feature _SMOOTHNESS_ALBEDO…
} Cutout shadows, directional and spotlight.Refactoring My LightingBefore we move on, let's tweak My Lighting a bit as well. Notice how we've used UnityObjectToClipPos to transform the vertex position in My Shadows. We can use this function in My Lighting as well, instead of performing a matrix multiplication ourselves. The UnityObjectToClipPos function also performs this multiplication, but uses the constant value 1 as the fourth position coordinate, instead of relying on the mesh data.Interpolators MyVertexProgram (VertexData v) { Interpolators
i.pos = UnityObjectToClipPos(v.vertex);…}The data supplied via the mesh is always 1, but the shader compiler doesn't know this. As a result, using a constant is more efficient. Beginning with version 5.6, Unity will give a performance warning when using an unoptimized multiplication with UNITY_MATRIX_MVP.Partial ShadowsTo also support shadows for the Fade and Transprant rendering modes, we have to add their keywords to the shader feature of or shadow caster pass. Like the other passes, the rendering feature now has four possible states.#pragma shader_feature_ _RENDERING_CUTOUT _RENDERING_FADE _RENDERING_TRANSPARENTThese two modes are semitransparent instead of cutout. So their shadows should be semitransparent as well. Let's define a convenient SHADOWS_SEMITRANSPARENT macro in My Shadows when this is the case.#if defined(_RENDERING_FADE) || defined(_RENDERING_TRANSPARENT)#define SHADOWS_SEMITRANSPARENT 1#endifNow we have to adjust the definition of SHADOWS_NEED_UV, so it also gets defined in the case of semitransparent shadows.#ifSHADOWS_SEMITRANSPARENT || defined(_RENDERING_CUTOUT) #if !defined(_SMOOTHNESS_ALBEDO)#define SHADOWS_NEED_UV 1
#endif#endifDitheringShadow maps contain the distance to surfaces that block light. Either the light is blocked at some distance, or it is not. Hence, there is no way to specify that light is partially blocked by semitransparent surfaces.What we can do, is clip part of the shadow surface. That's what we do for cutout shadows. But instead of clipping based on a threshold, we could clip fragments uniformly. For example, if a surface lets half the light through, we could clip every other fragment, using a checkerboard pattern. Overall, the resulting shadow will appear half as strong as a full shadow.We don't always have to use the same pattern. Depening on the alpha value, we can use a pattern with more or less holes. And if we mix these patterns, we can create smooth transitions of shadow density. Basically, we're using only two states to approximate a gradient. This technique is known as dithering.Unity contains a dither pattern atlas that we can use. It contains 16 different patterns of 4 by 4 pixels. It starts with a completely empty pattern. Each successive pattern fills one additional pixel, until there are seven filled. Then the pattern is inverted and reverses, until all pixels are filled.Dither patterns used by Unity.VPOSTo apply a dither patter to our shadow, we have to sample it. We cannot use the UV coordinates of the mesh, because those aren't uniform in shadow space. Instead, we'll need to use the screen-space coordinates of the fragment. As shadow maps are rendered from the point of view of the light, this aligns the patterns with the shadow map.The screen-space position of a fragment can be accessed in the fragment program, by adding a parameter with the VPOS semantic to it. These coordinates are not explicitly output by the vertex program, but the GPU can make them available to us.Unfortunately, the VPOS and SV_POSITION semantics don't play nice. On some platforms, they end up mapped to the same position semantic. So we cannot use both at the same time in our Interpolators struct. Fortunately, we only need to use SV_POSITION in the vertex program, while VPOS is only needed in the fragment program. So we can use a separate struct for each program.First, rename Interpolators to InterpolatorsVertex and adjust MyShadowVertexProgramaccordingly. Do not adjust MyShadowFragmentProgram.struct InterpolatorsVertex{ float4 position : SV_POSITION; #if SHADOWS_NEED_UV
float2 uv : TEXCOORD0; #endif
#if defined(SHADOWS_CUBE)
float3 lightVec : TEXCOORD1; #endif };…InterpolatorsVertexMyShadowVertexProgram (VertexData v) {InterpolatorsVertexi; #if defined(SHADOWS_CUBE)
i.position = UnityObjectToClipPos(v.position);
i.lightVec = (unity_ObjectToWorld, v.position).xyz - _LightPositionRange.xyz; #else
i.position = UnityClipSpaceShadowCasterPos(v.position.xyz, v.normal);
i.position = UnityApplyLinearShadowBias(i.position); #endif
#if SHADOWS_NEED_UV
i.uv = TRANSFORM_TEX(v.uv, _MainTex); #endif
return}Then create a new Interpolators struct for use in the fragment program. It is a copy of the other struct, except that it should contain UNITY_VPOS_TYPE vpos : VPOS instead of float4 positions : SV_POSITION when semitransparent shadows are needed. The UNITY_VPOS_TYPE macro is defined in HLSLSupport. It's usually a float4, except for Direct3D 9, which needs it to be a float2.struct InterpolatorsVertex { …}struct Interpolators {#if SHADOWS_SEMITRANSPARENTUNITY_VPOS_TYPE vpos : VPOS;#elsefloat4 positions : SV_POSITION;#endif#if SHADOWS_NEED_UVfloat2 uv : TEXCOORD0;#endif#if defined(SHADOWS_CUBE)float3 lightVec : TEXCOORD1;#endif};Do we need position at all in the fragment program?The vertex program needs to output its transformed position, but we don't have to access it in our fragment program. So technically we could leave it out of the struct. However, because all other fields of the struct are conditional, that could lead to an empty struct. The compiler can't always handle those, so we keep the position in there to prevent errors.DitheringTo access Unity's dither pattern texture, add a _DitherMaskLOD variable to My Shadows. The different patterns are stored in layers of a 3D texture, so its type has to be sampler3D instead of sampler2D.sampler3D _DitherMaskLOD;Sample this texture in MyShadowFragmentProgram, if we need semitransparent shadows. This is done via the tex3D function, which requires 3D coordinates. The third coordinate should be in the 0–1 range and is used to select a 3D slice. As there are 16 patterns, the Z coordinate of the first pattern is 0, the coordinate for the second pattern is 0.0625, the third is 0.128, and so on. Let's begin by always choosing the second pattern.float4 MyShadowFragmentProgram (Interpolators i) : SV_TARGET { float alpha = GetAlpha(i); #if defined(_RENDERING_CUTOUT)
(alpha - _AlphaCutoff); #endif
#if SHADOWS_SEMITRANSPARENTtex3D(_DitherMaskLOD, float3(i.vpos.xy, 0.0625));#endif…}The alpha channel of the dither texture is zero when a fragment should be discarded. So subtract a small value from it and use that to clip. #if SHADOWS_SEMITRANSPARENT
float dither =tex3D(_DitherMaskLOD, float3(i.vpos.xy, 0.0625)).a;(dither - 0.01);#endifTo actually see a pattern, we have to scale it. To get a good look at it, magnify it by a factor of 100, which is done by multiplying the position by 0.01. A spotlight shadow allows us to get a good look at it.
tex3D(_DitherMaskLOD, float3(i.vpos.xy * 0.01, 0.0625)).a;Uniform dithering, in fade mode.You can inspect all 16 dither patterns by increasing the Z coordinate in steps of 0.0625. The shadows get fully clipped at 0, and are fully rendered at 0.9375.Changing dither patterns.Approximating SemitransparencyInstead of using a uniform pattern, we have to base the selection of the dither pattern on the surface's alpha value. As full opacity is reached at 0.9375, multiply the alpha value by this factor, then use it as the Z coordinate.
tex3D(_DitherMaskLOD, float3(i.vpos.xy * 0.01,alpha * 0.9375)).a;Dithering based on alpha.The dithering now varies based on the surface opacity. To make it look more like a true shadow, we'll have to scale down the pattern size. Unity uses a factor of 0.25, so we'll use that as well.
tex3D(_DitherMaskLOD, float3(i.vpos.xy *0.25, alpha * 0.9375)).a;Scaled dithering.This looks a lot better, but it's not perfect. How obvious the dithering is depends on the resolution of the shadow map. The higher its resolution, the smaller and less obvious the patterns.Dithering works better with soft directional shadows. The screen-space filtering smudges the dithered fragments to such a degree that they're no longer obvious. The result is something that approaches actual semitransparent shadows. Hard and soft directional shadows with dithering.Unfortunately, dithering is not visually stable. When things move, you can get very obvious shadow swimming. Not just along the edge, but across the entire shadow!Swimming dithering.What about receiving shadows on semitransparent surfaces?Unfortunately, Unity does not support shadow casting on semitransparent surfaces. So materials using the Fade or Transparent rendering mode will not receive shadows. Cutout works fine, though.Optional Semitransparent ShadowsConsidering the limitations of semitransparent shadows, you might decide not to use them. You can entirely disable the shadows of an object via the Cast Shadows mode of its Mesh Renderer component. However, it could be that cutout shadows work just fine for a semitransparent object. For example, when a significant portion of its surface is fully opaque. So let's make it possible to choose between both types of shadows.To support this choice, add a shader feature to the shadow caster pass for a new keyword, _SEMITRANSPARENT_SHADOWS.
#pragma shader_feature _SEMITRANSPARENT_SHADOWSIn My Shadows, only define SHADOWS_SEMITRANSPARENT if the _SEMITRANSPARENT_SHADOWS shader keyword is set.#if defined(_RENDERING_FADE) || defined(_RENDERING_TRANSPARENT)
#if defined(_SEMITRANSPARENT_SHADOWS)#define SHADOWS_SEMITRANSPARENT 1
#endif#endifIf the new shader feature is not enabled, then we should fall back to cutout shadows. We can do this by manually defining _RENDERING_CUTOUT.#if defined(_RENDERING_FADE) || defined(_RENDERING_TRANSPARENT)
#if defined(_SEMITRANSPARENT_SHADOWS)
#define SHADOWS_SEMITRANSPARENT 1
#else#define _RENDERING_CUTOUT#endif #endifBecause the new shader feature isn't enabled yet, we now get cutout shadows when using the Fade or Transparent rendering mode.Fade rendering, with cutout shadows.Toggling SemitransparencyTo enable semitransparent shadows again, we have to add an option for it to our custom shader UI. So add a DoSemitransparentShadows method to MyLightingShaderGUI. void DoSemitransparentShadows () {}We only need to show this option when using the Fade or Transparent rendering mode. We know which mode we're using inside DoRenderingMode. So invoke DoSemitransparentShadowsat the end of this method, if needed. void DoRenderingMode () {
…if (mode == RenderingMode.Fade || mode == RenderingMode.Transparent) {DoSemitransparentShadows();}}As this is a binary choice, we can represent it with a toggle button. Because the label Semitransparent Shadows is wider than Unity's default inspector window width, I've abbreviated it. For clarity, I gave it a tooltip that isn't abbreviated. void DoSemitransparentShadows () {bool semitransparentShadows =.Toggle(MakeLabel("Semitransp. Shadows", "Semitransparent Shadows"),IsKeywordEnabled("_SEMITRANSPARENT_SHADOWS"));}Semitransparent shadows checkbox.Like with the other keywords, check whether the user makes a change and set the keyword accordingly. void DoSemitransparentShadows () {.BeginChangeCheck();bool semitransparentShadows = .Toggle(
MakeLabel("Semitransp. Shadows", "Semitransparent Shadows"),
IsKeywordEnabled("_SEMITRANSPARENT_SHADOWS")
);if (.EndChangeCheck()) {SetKeyword("_SEMITRANSPARENT_SHADOWS", semitransparentShadows);}}Showing Alpha Cutoff for ShadowsWhen using cutout shadows, we might like to change the Alpha Cutoff threshold. Currently, it only shows up in our UI when using the Cutout rendering mode. However, it must now also be accessible in Fade and Transparent mode, when not using semitransparent shadows. We can support this by setting shouldShowAlphaCutoff to true in DoSemitransparentShadows, when appropriate. void DoSemitransparentShadows () {
…if (!semitransparentShadows) {shouldShowAlphaCutoff = true;}}Alpha cutoff appears when needed.The next tutorial is .
Unity 渲染教程(十二):半透明材质的阴影
版权所有,禁止匿名转载;禁止商业使用;禁止个人使用。
译者: 崔嘉艺(milan21)
审校:王磊(未来的未来)支持剪切造成的阴影。使用抖动。对半透明材质造成的阴影进行近似。在半透明材质的阴影和剪切生成的阴影之间切换。这是关于渲染基础的系列教程的第十二部分。在前面的部分里我们已经可以渲染半透明表面了,但我们还没有实现半透明表面的阴影。现在我们来解决这个问题。这个教程是使用Unity 5.5.0f3开发的。 当物体淡入的时候,它们的阴影也要相应的淡入。剪切造成的阴影目前,我们透明材质的阴影总是像是由不透明材质投射的一样,因为这是我们的着色器所假设的一个条件。因此,阴影可能会显得很奇怪,直到你意识到你看到的是一个不透明对象所投射的阴影。在方向光生成的阴影情况下,这也可以导致不可见的几何体遮挡阴影。 在不透明渲染模式和剪切渲染模式下,所得到的方向阴影是相同的。在聚光灯或点光源生成光影的情况下,你会得到一个实心的阴影。实心的聚光灯阴影。重构我的阴影为了把透明度考虑进来,我们必须访问阴影投射着色器渲染通道中的透明度值。这意味着我们需要对反射率纹理进行采样。但是,当使用不透明渲染模式的时候,我们不需要对反射率纹理进行采样。所以我们需要为我们的阴影使用多个着色器变体。现在我们有两个版本的阴影程序。 一个版本用于立方体阴影贴图,这是点光源所需的,一个用于其他光源类型。现在我们需要混合更多的变体。为了使混合更多的变体更容易,我们将重写My Shadow这个导入文件。我们将为所有变体使用插值器,并创建单个顶点和片段程序。首先,将Interpolator的定义移出条件块。然后使光向量成为条件。123456789101112131415161718192021struct VertexData {
float4 position : POSITION;
float3 normal : NORMAL; };
struct Interpolators {
float4 position : SV_POSITION;
#if defined(SHADOWS_CUBE)
float3 lightVec : TEXCOORD0;
#endif };接下来,写一个新的顶点程序,其中包含两个不同版本的副本。非立方体的代码必须稍微调整以使用新的Interpolator输出。1234567891011121314151617181920212223Interpolators MyShadowVertexProgram (VertexData v) {
#if defined(SHADOWS_CUBE)
i.position = UnityObjectToClipPos(v.position);
i.lightVec =
mul(unity_ObjectToWorld, v.position).xyz - _LightPositionRange.
i.position = UnityClipSpaceShadowCasterPos(v.position.xyz, v.normal);
i.position = UnityApplyLinearShadowBias(i.position);
return i; }对片段程序执行相同操作。然后摆脱旧的条件程序12345678910111213141516171819202122232425float4 MyShadowFragmentProgram (Interpolators i) : SV_TARGET {
#if defined(SHADOWS_CUBE)
float depth = length(i.lightVec) + unity_LightShadowBias.x;
depth *= _LightPositionRange.w;
return UnityEncodeCubeShadowDepth(depth);
裁剪阴影片段我们首先处理剪切造成的阴影。 我们通过丢弃片段在阴影中切孔,就像我们在剪切渲染模式中的其他渲染过程中所做的那样。为达到这个目的,我们需要材料的色调、反射率纹理和透明度截止阈值这些信息。在My Shadows的顶部为它们添加相应的变量。1234567891011#include "UnityCG.cginc"
float4 _T sampler2D _MainT float4 _MainTex_ST; float _AlphaC因此,当我们使用剪切渲染模式的时候,我们必须对反射率纹理进行采样。实际上,当我们不使用反射率纹理的透明度值来确定平滑度的时候我们必须这样做。当满足这些条件的时候,我们必须将UV坐标传递给片段程序。 同时如果满足这些条件的话,我们将SHADOWS_NEED_UV定义为1。这样,我们可以方便地使用#if SHADOWS_NEED_UV123456789#include "UnityCG.cginc"
#if defined(_RENDERING_CUTOUT) && !defined(_SMOOTHNESS_ALBEDO)
#define SHADOWS_NEED_UV 1 #endif将UV坐标添加到顶点输入数据。我们不需要让顶点输入数据成为有条件的。然后有条件地将UV坐标添加到内插值器里面去。1234567891011121314151617181920212223242526272829struct VertexData {
float4 position : POSITION;
float3 normal : NORMAL;
float2 uv : TEXCOORD0; };
struct Interpolators {
float4 position : SV_POSITION;
#if SHADOWS_NEED_UV
float2 uv : TEXCOORD0;
#if defined(SHADOWS_CUBE)
float3 lightVec : TEXCOORD1;
#endif };需要的情况下,我们将UV坐标传递到顶点程序中的插值器中去。123456789101112131415Interpolators MyShadowVertexProgram (VertexData v) {
#if SHADOWS_NEED_UV
i.uv = TRANSFORM_TEX(v.uv, _MainTex);
return i; }将GetAlpha方法从My Lighting复制到My Shadows里面。在这里,纹理是否被采样取决于SHADOWS_NEED_UV。 所以要检查的是SHADOWS_NEED_UV,而不是是否定义了_SMOOTHNESS_ALBEDO。 我对区别进行了标记。12345678910111213float GetAlpha (Interpolators i) {
float alpha = _Tint.a;
#if SHADOWS_NEED_UV
alpha *= tex2D(_MainTex, i.uv.xy).a;
}现在我们可以获取片段程序中的透明度值,并在剪切渲染模式下使用它进行裁剪。123456789101112131415float4 MyShadowFragmentProgram (Interpolators i) : SV_TARGET {
float alpha = GetAlpha(i);
#if defined(_RENDERING_CUTOUT)
clip(alpha - _AlphaCutoff);
… }要使其实际工作,请将_RENDERING_CUTOUT和_SMOOTHNESS_ALBEDO这两个着色器功能添加到My FirstLighting着色器的阴影投射渲染通道里面。123456789101112131415161718192021222324252627Pass {
"LightMode" = "ShadowCaster"
#pragma target 3.0
#pragma shader_feature _RENDERING_CUTOUT
#pragma shader_feature _SMOOTHNESS_ALBEDO
… } 剪切所形成的阴影,分别在方向光和聚光灯下的效果。重构MyLighting在我们继续往下走之前,让我们稍微调整下MyLighting。注意我们如何使用UnityObjectToClipPos来转换My Shadows中的顶点位置。我们可以在My Lighting中使用这个函数,而不是自己执行矩阵乘法。 UnityObjectToClipPos函数也执行此乘法,但使用常量值1作为第四个位置坐标,而不是依赖网格数据。1234567891011Interpolators MyVertexProgram (VertexData v) {
i.pos = UnityObjectToClipPos(v.vertex);
… }通过网格提供的数据总是1,但着色器的编译器不知道这一点。因此,使用常数更加的有效。从版本5.6开始,当使用UNITY_MATRIX_MVP进行未优化的乘法操作的时候,Unity将给出性能警告。工程文件下载地址:。部分阴影为了能够支持渐变和透明渲染模式的阴影,我们必须将它们的关键字添加到阴影投射渲染通道的着色器功能里面。像其他渲染通道一样,渲染功能现在有四个可能的状态。1#pragma shader_feature _ _RENDERING_CUTOUT _RENDERING_FADE _RENDERING_TRANSPARENT这两种模式是半透明的而不是剪切。所以他们的影子也应该是半透明的。让我们在My Shadows中定义一个方便的SHADOWS_SEMITRANSPARENT宏。12345#if defined(_RENDERING_FADE) || defined(_RENDERING_TRANSPARENT)
#define SHADOWS_SEMITRANSPARENT 1 #endif现在我们必须调整SHADOWS_NEED_UV的定义,所以它也在半透明阴影的情况下被定义。123456789#if SHADOWS_SEMITRANSPARENT || defined(_RENDERING_CUTOUT)
#if !defined(_SMOOTHNESS_ALBEDO)
#define SHADOWS_NEED_UV 1
#endif #endif抖动阴影贴图包含着到阻挡光的表面的距离。或者光被阻挡在一定距离之外,或者没有发生阻挡。因此,没有办法指定光被半透明表面部分的阻挡。我们可以做的,是裁剪阴影表面的一部分。这就是我们在剪切生成的阴影里面所做的事情。但是,这次的裁剪不是基于阈值进行裁剪,我们可以均匀地裁剪片段。举个简单的例子来说,如果一个表面让一半的光通过,我们可以使用棋盘模式剪切每个片段。总的来说,生成的阴影将显示为完整阴影的一半。我们不总是使用相同的模式。根据透明度值,我们可以使用具有更多孔或是更少孔的模式。如果我们混合这些模式,我们可以创建阴影密度的平滑过渡。基本上,我们只使用两个状态来对梯度进行模拟。这种技术被称为抖动。Unity包含了一个可以使用的抖动模式图集。它包含16个不同的4×4像素的模式。它从一个完全空的模式开始。接下去的每个模式会填充一个额外的像素,直到有七个被填充。然后,图案被反转和互换,直到所有像素被填充。Unity使用的抖动模式。VPOS要将抖动模式应用到我们的阴影上去,我们必须对它进行采样。我们不能使用网格的UV坐标,因为那些坐标在阴影空间中不均匀。相反,我们需要使用片段的屏幕空间坐标。因为阴影贴图是从光在视野空间的位置开始渲染的,,这将使模式与阴影贴图对齐。通过添加具有VPOS语义的参数,可以在片段程序中访问片段的屏幕空间位置。这些坐标不是由顶点程序显式输出的,但是图形处理器可以使它们对我们可用。不幸的是,VPOS语义和SV_POSITION语义不起作用。在一些平台上,他们最终映射到相同的位置语义。所以我们不能同时在Interpolators结构中使用这两个语义。幸运的是,我们只需要在顶点程序中使用SV_POSITION,而VPOS只需要用在片段程序中。所以我们可以为每个程序使用一个单独的结构体。首先,将Interpolators重命名为InterpolatorsVertex,然后调整MyShadowVertexProgram。不要调整MyShadowFragmentProgram。12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455struct InterpolatorsVertex {
float4 position : SV_POSITION;
#if SHADOWS_NEED_UV
float2 uv : TEXCOORD0;
#if defined(SHADOWS_CUBE)
float3 lightVec : TEXCOORD1;
InterpolatorsVertex MyShadowVertexProgram (VertexData v) {
InterpolatorsV
#if defined(SHADOWS_CUBE)
i.position = UnityObjectToClipPos(v.position);
i.lightVec =
mul(unity_ObjectToWorld, v.position).xyz - _LightPositionRange.
i.position = UnityClipSpaceShadowCasterPos(v.position.xyz, v.normal);
i.position = UnityApplyLinearShadowBias(i.position);
#if SHADOWS_NEED_UV
i.uv = TRANSFORM_TEX(v.uv, _MainTex);
return i; }然后创建一个新的Interpolators结构体在片段程序中使用。它是另一个结构的副本,当需要半透明阴影的时候,它应该包含UNITY_VPOS_TYPE vpos:VPOS而不是float4 positions:SV_POSITION。 UNITY_VPOS_TYPE宏在HLSLSupport中进行定义。它通常是一个float4类型,除了Direct3D 9,Direct3D 9需要的是一个float2类型。1234567891011121314151617181920212223242526272829303132333435struct InterpolatorsVertex {
struct Interpolators {
#if SHADOWS_SEMITRANSPARENT
UNITY_VPOS_TYPE vpos : VPOS;
float4 positions : SV_POSITION;
#if SHADOWS_NEED_UV
float2 uv : TEXCOORD0;
#if defined(SHADOWS_CUBE)
float3 lightVec : TEXCOORD1;
#endif }; 我们在片段程序中需要position 吗?顶点程序需要输出它的变换位置,但我们不必在我们的片段程序中访问它。 所以技术上来讲我们可以把它从结构体移除去。然而,因为结构体的所有其他字段是有条件的,这可能导致一个空的结构。编译器不能总是处理这些空的情况,所以我们把这个信息保留在那里,以防止错误。抖动要访问Unity的抖动模式纹理,请将_DitherMaskLOD变量添加到My Shadows。不同的模式存储在3D纹理的层中,因此它的类型必须是sampler3D而不是sampler2D。1sampler3D _DitherMaskLOD;如果我们需要半透明阴影的话,需要在MyShadowFragmentProgram中对此纹理进行采样。这是通过tex3D函数完成的,这个函数需要的是3D坐标。 第三个坐标应该在0-1范围内,并且用于选择3D切片。由于有16个模式,第一个模式的Z坐标为0,第二个模式的坐标为0.0625,第三个模式的坐标为0.128,以此类推。让我们开始总是选择第二个模式。1234567891011121314151617181920212223float4 MyShadowFragmentProgram (Interpolators i) : SV_TARGET {
float alpha = GetAlpha(i);
#if defined(_RENDERING_CUTOUT)
clip(alpha - _AlphaCutoff);
#if SHADOWS_SEMITRANSPARENT
tex3D(_DitherMaskLOD, float3(i.vpos.xy, 0.0625));
… }当应该丢弃片段的时候,抖动纹理的透明度通道为零。所以从中减去一个小的值,并使用它来进行裁剪。123456789#if SHADOWS_SEMITRANSPARENT
float dither =
tex3D(_DitherMaskLOD, float3(i.vpos.xy, 0.0625)).a;
clip(dither - 0.01); #endif要实际看到一个模式,我们必须对这个模式进行放缩。为了好好看看这个模式,我们将这个模式放大100倍,这是通过将位置乘以0.01来做到的。聚光灯的阴影让我们可以很好地看看这个模式。1tex3D(_DitherMaskLOD, float3(i.vpos.xy * 0.01, 0.0625)).a;在淡入模式的均匀抖动效果。你可以通过以0.0625的步长增大Z坐标来检查所有16种抖动模式。阴影在0的时候被完全裁剪,在0.9375的时候完全渲染。对半透明效果的近似不是使用一个统一的模式,我们必须基于表面的透明度值来选择抖动模式。当达到0.9375的时候,是完全不透明的状态,将透明度值乘以该因子,然后将得到的值用作Z坐标。1tex3D(_DitherMaskLOD, float3(i.vpos.xy * 0.01, alpha * 0.9375)).a;基于透明度的抖动。抖动现在基于表面不透明度的值会发生变化。为了使它看起来更像一个真正的阴影,我们将不得不对模式的大小进行放缩。Unity使用0.25的因子,因此我们也将使用这个因子。1tex3D(_DitherMaskLOD, float3(i.vpos.xy * 0.25, alpha * 0.9375)).a;缩放抖动。这看起来好多了,但它不是完美的。抖动的明显程度取决于阴影贴图的分辨率。分辨率越高,模式越小且越不明显。抖动在软方向阴影的情况工作的更好。屏幕空间的过滤将抖动片段模糊到模式不再明显的程度。得到的结果是接近实际的半透明阴影效果。 带有抖动的硬方向阴影和软方向阴影。不幸的是,抖动在视觉上不稳定。当物体移动的时候,你可以得到非常明显的阴影游动的效果。不只是沿着边缘,而是整个阴影都有这个情况!阴影游动的效果见。在半透明表面上要接收阴影该怎么办?不幸的是,Unity不支持在半透明表面上进行阴影投射。因此,使用淡入渲染模式或是透明渲染模式的材质不会接收阴影。但是剪切渲染模式可以正常工作。工程文件下载地址:。可选的半透明阴影考虑到半透明阴影的限制,你可能决定不使用它们。你可以通过其网格渲染器组件的“投射阴影”模式来完全禁用一个物体的阴影。但是,它可能是剪切生成的阴影工作正好对一个半透明的物体来说非常适合。举个简单的例子来说,当这个物体的表面占非常显着的部分是完全不透明的时候。因此,让我们可以在两种类型的阴影之间进行选择。为了支持这个选择,在阴影投射渲染通道里面通过一个新的关键字_SEMITRANSPARENT_SHADOWS来添加一个着色器特性。1#pragma shader_feature _SEMITRANSPARENT_SHADOWS在My Shadows中,只有在设置了_SEMITRANSPARENT_SHADOWS着色器关键字的情况下才会定义SHADOWS_SEMITRANSPAREN。123456789#if defined(_RENDERING_FADE) || defined(_RENDERING_TRANSPARENT)
#if defined(_SEMITRANSPARENT_SHADOWS)
#define SHADOWS_SEMITRANSPARENT 1
#endif #endif如果没有启用新的着色器功能,那么我们应该回退到剪切生成的阴影处理那里。我们可以通过手动定义_RENDERING_CUTOUT来完成这个操作。12345678910111213#if defined(_RENDERING_FADE) || defined(_RENDERING_TRANSPARENT)
#if defined(_SEMITRANSPARENT_SHADOWS)
#define SHADOWS_SEMITRANSPARENT 1
#define _RENDERING_CUTOUT
#endif #endif因为新的着色器功能尚未启用,我们现在使用淡入渲染模式或是透明渲染模式的时候,可以得到剪切生成的阴影。使用淡入渲染模式,得到的剪切生成的阴影。切换半透明度为了再次启用半透明阴影,我们必须为我们的自定义着色器UI添加一个选项。 所以添加一个DoSemitransparentShadows方法到MyLightingShaderGUI里面。123void DoSemitransparentShadows () { }我们只需要在使用淡入渲染模式或是透明渲染模式的时候显示这个选项。我们知道在DoRenderingMode里面使用了哪种模式。因此,如果需要的话,在此方法的结尾调用DoSemitransparentShadows。12345678910111213void DoRenderingMode () {
if (mode == RenderingMode.Fade || mode == RenderingMode.Transparent) {
DoSemitransparentShadows();
} }由于这是一个二进制的选择,我们可以用切换按钮来表示这个选择。因为标签Semitransparent Shadows 比Unity的默认检视器窗口的宽度要宽,我在这里使用了缩写。为了清楚起见,我给了它一个没有缩写的工具提示。12345678910111213void DoSemitransparentShadows () {
bool semitransparentShadows =
EditorGUILayout.Toggle(
MakeLabel("Semitransp. Shadows", "Semitransparent Shadows"),
IsKeywordEnabled("_SEMITRANSPARENT_SHADOWS")
); } 半透明阴影复选框。与其他关键字一样,会去检查用户是否进行了更改并相应地设置了关键字。123456789101112131415161718192021void DoSemitransparentShadows () {
EditorGUI.BeginChangeCheck();
bool semitransparentShadows =
EditorGUILayout.Toggle(
MakeLabel("Semitransp. Shadows", "Semitransparent Shadows"),
IsKeywordEnabled("_SEMITRANSPARENT_SHADOWS")
if (EditorGUI.EndChangeCheck()) {
SetKeyword("_SEMITRANSPARENT_SHADOWS", semitransparentShadows);
} }显示阴影的透明度截止阈值当使用剪切生成的阴影的时候,我们可能想更改透明度截止阈值。目前,透明度截止阈值仅在使用剪切渲染模式的时候会显示在我们的UI中。但是,当不使用半透明阴影的时候,它现在也必须可以在淡入渲染模式和透明渲染模式下访问。我们可以通过在DoSemitransparentShadows中设置shouldShowAlphaCutoff为true来支持这一点。1234567891011void DoSemitransparentShadows () {
if (!semitransparentShadows) {
shouldShowAlphaCutoff = true;
} }在需要的时候显示透明度截止阈值。 这个系列的下一篇教程是:延迟渲染。工程文件下载地址:。PDF下载地址:。 【版权声明】原文作者未做权利声明,视为共享知识产权进入公共领域,自动获得授权。
分类:程序新手圈
请勿发表无意义的内容请勿发表重复内容请勿发表交易类内容禁止发表广告宣传贴请使用文明用语其它
淫秽色情政治倾向人身攻击抄袭剽窃广告刷屏恶意挖坟冒充他人其它
登录后参与讨论。点击}

我要回帖

更多关于 unity 物体没有阴影 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信