前置:UnityGI1:光照烘培

一、着色器应用 Lightmaps

对于烘焙了 lightmaps 的场景,使用了自己的着色器可能得不到正确的结果

1.1 关键字 LIGHTMAP_ON

当着色器应用 lightmaps 时,内置关键字 LIGHTMAP_ON 会起作用,同时不会再包含顶点光照,也就是对应的 VERTEXLIGHT_ON 关键字必然会失效,因此我们可以添加如下的 #pragma 指令:

#pragma multi_compile __ LIGHTMAP_ON VERTEXLIGHT_ON
//or #pragma multi_compile __ LIGHTMAP_ON

用于采样 lightmaps 的纹理坐标会在第二套 uv 坐标中:

struct appdata
{#if defined(LIGHTMAP_ON)float2 lightMapUV: TEXCOORD1;           //光照贴图uv坐标float2 dynLightMapUV: TEXCOORD2;        //动态光照贴图uv坐标#endif
}
//……
struct v2f
{#if defined(LIGHTMAP_ON)float2 lightMapUV: TEXCOORD1;           //光照贴图uv坐标float2 dynLightMapUV: TEXCOORD2;        //动态光照贴图uv坐标#endif
}

在顶点着色器中,必须要对 lightmaps 的 uv 坐标进行偏移和变换,并且这个偏移&变换对于每个对象都是不同的,而不是对于材质,想想看一张光照贴图里面存储了多少物体的光照信息

除此之外,我们不能直接使用 TRANSFORM_TEX 宏进行纹理的偏移,因为这个宏假定 lightmaps 的偏移&变换数据存储在 unity_Lightmap_ST 中,而实际上,这个数据是存储在 unity_LightmapST 上的

#if defined(LIGHTMAP_ON)o.lightmapUV = v.lightmapUV * unity_LightmapST.xy + unity_LightmapST.zw;
#endif

1.2 lightmaps 采样

到这一步并没有结束,紧接着是片段着色器:

#if defined(LIGHTMAP_ON)float3 indirectDiffuse = DecodeLightmap(UNITY_SAMPLE_TEX2D(unity_Lightmap, i.lightmapUV));
#elsefloat3 indirectDiffuse = max(0, ShadeSH9(float4(normal, 1)));;
#endif

片段着色器很简单,保存后就可以看到正确的效果(当然前提条件是你的着色器本身可以得到一个还可以的效果)

其中的 indirectDiffuse 为间接光漫反射,在一般情况下 shader 中最终得到光照结果为:直接光漫反射 + 直接光镜面反射 + 间接光漫反射 + 间接光镜面反射

对于上面的代码:

  • lightmaps 数据被当作是间接光
  • 应用 lightmaps 的往往是场景中的静态物体,因此一般情况下只需要考虑 lightmaps,不需要也不会考虑顶点光照以及球谐光照这些
  • 如果你的物体是场景中的动态物体,不应用 lightmaps,那么就需要采样周边光照探针来获得间接漫反射光照,这对应着上面的 ShadeSH9 方法

Untiy 在定义 lightmaps 采样器时使用了宏 UNITY_DECLARE_TEX2D,这考虑了平台差异,因此我们在采样时也要用对应的 UNITY_SAMPLE_TEX2D 宏,除此之外,由于光照贴图的数据并非直观的颜色数据(例如 RGBM,M 为强度),需要解码才可以得到正确的结果,所以我们在采样后还需调用 Unity 的 DecodeLightmap 方法

二、透明应用

由于 lightmap 的计算本质上不是实时渲染,所以在烘焙的过程中不会使用我们之前的着色器来完成工作,这个时候我们需要让光照烘焙系统知道哪些物体是透明的,就需要用一些约定

《UnityGI1:光照烘培》这一章的 4.2 节给出了一个方法,不过透明不止会影响环境光,还会影响阴影:

透明物体会透过部分光照,因此阴影应该是比较“浅”的

这时就还需要告诉光照烘焙系统每一个物体的透明度:一个约定是通过着色器 _Color 属性的 alpha 通道以及主纹理 _MainTex 的 alpha 通道

Properties
{_Color("Tint", Color) = (1, 1, 1, 1)_MainTex("Albedo", 2D) = "white" {}
}
//……
float GetAlpha(v2f i)
{float alpha = tex2D(_MainTex, i.uv.xy).a * _Color.a;return alpha;
}

2.1 自定义 ShaderGUI

如果可以的话,可以一个 Shader 同时负责渲染透明和不透明的物体

Properties
{_Color("Tint", Color) = (1, 1, 1, 1)_MainTex("Albedo", 2D) = "white" {}//……[Enum(Off, 0, Cutoff, 1, blend, 2)] _AlphaMode("AlphaMode", float) = 0[Enum(UnityEngine.Rendering.CullMode)] _Cull("Cull", float) = 2[HideInInspector] _Mode("Blend Mode", float) = 0[HideInInspector][Enum(UnityEngine.Rendering.BlendMode)] _SrcBlend("Src Blend", float) = 1[HideInInspector][Enum(UnityEngine.Rendering.BlendMode)] _DstBlend("Dst Blend", float) = 0[Toggle] _ZWrite("ZWrite", float) = 0[Enum(UnityEngine.Rendering.CompareFunction)] _ZTest("ZTest", float) = 4
}
//……
Pass
{Blend [_SrcBlend] [_DstBlend]ZWrite [_ZWrite]ZTest [_ZTest]Cull [_Cull]CGPROGRAM#pragma multi_compile __ LIGHTMAP_ON VERTEXLIGHT_ON#pragma shader_feature __ _ALPHAMODE_CUTOUT _ALPHAMODE_BLEND//……CGEND
}

这些基本参数全部暴露到材质面板中就可以,如果觉得材质面板比较乱,又或者发现没法动态调整 RenderType 的 Tag,也可以自己写一个 ShaderGUI:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;//允许多个对象同时编辑
[CanEditMultipleObjects]
public class PBRGUI: ShaderGUI
{public enum AlphaMode{Opaque = 0,AlphaTest = 1,Transparent = 2}MaterialProperty color = null;MaterialProperty specularColor = null;MaterialProperty mainTex = null;MaterialProperty metallic = null;MaterialProperty glossiness = null;MaterialProperty LUT = null;MaterialProperty alphaMode = null;MaterialProperty cull = null;MaterialProperty srcBlend = null;MaterialProperty dstBlend = null;MaterialProperty zwrite = null;MaterialProperty ztest = null;MaterialProperty cutoff = null;MaterialEditor m_MaterialEditor = null;Material m_Material = null;public void FindProperties(MaterialProperty[] props){color = FindProperty("_Color", props);mainTex = FindProperty("_MainTex", props);specularColor = FindProperty("_SpecularColor", props);metallic = FindProperty("_Metallic", props);glossiness = FindProperty("_Glossiness", props);LUT = FindProperty("_LUT", props);cull = FindProperty("_Cull", props);srcBlend = FindProperty("_SrcBlend", props);dstBlend = FindProperty("_DstBlend", props);zwrite = FindProperty("_ZWrite", props);ztest = FindProperty("_ZTest", props);alphaMode = FindProperty("_AlphaMode", props);cutoff = FindProperty("_Cutoff", props);}public override void OnGUI(MaterialEditor materialEditor, MaterialProperty[] props){FindProperties(props);m_MaterialEditor = materialEditor;m_Material = materialEditor.target as Material;//设置颜色框和纹理框为默认规则大小m_MaterialEditor.SetDefaultGUIWidths();m_MaterialEditor.TexturePropertySingleLine(new GUIContent("Albedo(RGB)"), mainTex, FindProperty("_Color", props));m_MaterialEditor.ShaderProperty(metallic, "金属");m_MaterialEditor.ShaderProperty(glossiness, "光滑");m_MaterialEditor.ShaderProperty(specularColor, "镜面色");m_MaterialEditor.TexturePropertySingleLine(new GUIContent("LUT"), mainTex);EditorGUILayout.Space();AlphaControl();EditorGUILayout.Space();CullAndTest();EditorGUILayout.Space();Instancing();DoubleGI();QueueControl();}void AlphaControl(){EditorGUI.BeginChangeCheck();var mode = (AlphaMode)EditorGUILayout.EnumPopup("透明模式", (AlphaMode)alphaMode.floatValue);//和BeginChangeCheck对应,当用户对材质的透明模式进行修改后,会自动执行方法里的代码if (EditorGUI.EndChangeCheck()){m_MaterialEditor.RegisterPropertyChangeUndo("Alpha Mode");alphaMode.floatValue = (float)mode;if (alphaMode.floatValue < 2){srcBlend.floatValue = (int)UnityEngine.Rendering.BlendMode.One;dstBlend.floatValue = (int)UnityEngine.Rendering.BlendMode.Zero;zwrite.floatValue = 1;if (alphaMode.floatValue == 1){//透明剔除m_Material.EnableKeyword("_ALPHAMODE_CUTOUT");m_Material.DisableKeyword("_ALPHAMODE_BLEND");m_Material.SetOverrideTag("RenderType", "TransparentCutout");m_Material.renderQueue = Mathf.Min(2500, m_Material.renderQueue);}else{//不透明m_Material.DisableKeyword("_ALPHAMODE_CUTOUT");m_Material.DisableKeyword("_ALPHAMODE_BLEND");m_Material.SetOverrideTag("RenderType", "Opaque");m_Material.renderQueue = Mathf.Min(2400, m_Material.renderQueue);}}else{srcBlend.floatValue = (int)UnityEngine.Rendering.BlendMode.SrcAlpha;dstBlend.floatValue = (int)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha;zwrite.floatValue = 0;//半透明m_Material.DisableKeyword("_ALPHAMODE_CUTOUT");m_Material.EnableKeyword("_ALPHAMODE_BLEND");m_Material.SetOverrideTag("RenderType", "Transparent");m_Material.renderQueue = Mathf.Max(3000, m_Material.renderQueue);}EditorUtility.SetDirty(m_Material);}//透明度测试if (alphaMode.floatValue == 1){//缩进EditorGUI.indentLevel += 2;m_MaterialEditor.ShaderProperty(cutoff, "透明剔除阈值");EditorGUI.indentLevel -= 2;}///透明度混合if (alphaMode.floatValue == 2){}}void CullAndTest(){m_MaterialEditor.ShaderProperty(cull, "剔除");m_MaterialEditor.ShaderProperty(ztest, "深度测试");m_MaterialEditor.ShaderProperty(zwrite, "深度写入");}void Instancing(){m_MaterialEditor.EnableInstancingField();}void DoubleGI(){m_MaterialEditor.LightmapEmissionProperty();m_MaterialEditor.DoubleSidedGIField();}void QueueControl(){m_MaterialEditor.RenderQueueField();}
}

想要使用自定义的材质面板,只需要在 Shader 的最下面声明一下就OK:

FallBack "Specular"
CustomEditor "PBRGUI"

搞定

2.2 透明度测试

透明度测试也有一样的问题,对于透明度的透明剔除阈值需要命名为 _Cutoff,需要注意的是 lightmaps 对开启了透明度测试的物体的剔除模式默认为不剔除

三、MetaPass

到此为止,可能会发现使用了自己的着色器的物体确实得到了一个正确的效果,但是并没有体现出间接光:

假定地面为一个纯红色的自发光光源,可见其它物体并没有呈现红色的表面效果

这是少了 MetaPass 的原因,如果你是使用的表面着色器,又或者是自带的 UnityStandard,内部都自带 MetaPass

所以这还需要自己写一个 MetaPass

3.1 MetaPass 和 Enlighten 烘焙系统

Unity5 后的版本将烘焙系统由 Beast 换成了 Enlighten,Enlighten 需要 Unity 提供材质的反射率(Albdeo)、高光(SpecularColor)和自发光(emissive)纹理用来计算间接光照,而这两个贴图都是 Unity 自己在 GPU 上渲染得到的,这就需要提供一个相应的 Pass 来让 Unity 用来进行这种渲染,就是 MetaPass 的作用,它专门负责给光照映射和动态全局光照提取表面信息

Pass
{Tags { "LightMode" = "Meta" }Cull OffCGPROGRAM#pragma vertex vert_meta#pragma fragment frag_meta#include "CGINC/SceneMeta.cginc"ENDCG
}

代码量不会小,所以这里可以将主体部分写在单独的 cginc 文件中,不过需要注意一点,由于子 Pass 设置了 LightMode 为 Meta,那么就需要确保 SubShader 不要去设置 LightMode,这是一个坑

MetaPass.cginc 完整代码如下:

#ifndef SCENE_META_INCLUDE
#define SCENE_META_INCLUDE
#define UNITY_PASS_META 1#include "UnityCG.cginc"
#include "UnityMetaPass.cginc"
#include "UnityStandardUtils.cginc"float4 _Color;
//float4 _SpecularColor;
float4 _EmissionColor;
float _Glossiness;
float _Metallic;
float _Cutoff;
//float _Ior;
//sampler2D _LUT;
sampler2D _MainTex;
sampler2D _EmissionX;float4 _MainTex_ST;
float4 _EmissionX_ST;struct appdata_meta
{float4 vertex: POSITION;float2 uv: TEXCOORD0;float2 uv1: TEXCOORD1;float2 uv2: TEXCOORD2;
};struct v2f_meta
{float4 uv: TEXCOORD0;float4 pos: SV_POSITION;
};v2f_meta vert_meta(appdata_meta v)
{v2f_meta o;UNITY_INITIALIZE_OUTPUT(v2f_meta, o);o.pos = UnityMetaVertexPosition(v.vertex, v.uv1, v.uv2, unity_LightmapST, unity_DynamicLightmapST);o.uv.xy = TRANSFORM_TEX(v.uv, _MainTex);return o;
}float3 GetAlbedo(v2f_meta i)
{float3 albedo = tex2D(_MainTex, i.uv.xy).rgb * _Color.rgb;return albedo;
}
float3 GetEmission(v2f_meta i)
{float3 emission = tex2D(_EmissionX, i.uv.xy).rgb + _EmissionColor.rgb;return min(emission, 1);
}//光照贴图的反照率应该就是漫反射颜色,但是粗糙的金属仍然会在周围散射相当多的光(尽管它的漫反射颜色是黑色的),所以这里最好也要考虑这一点
half3 GetLightmappingAlbedo(half3 diffuse, half3 specular, half smoothness)
{half roughness = 1 - smoothness;roughness *= roughness;half3 res = diffuse;res += specular * roughness * 0.5;return res;
}float4 frag_meta(v2f_meta i): SV_Target
{half3 albedo = GetAlbedo(i);half oneMinusReflectivity;half3 specColor;half3 diffColor = DiffuseAndSpecularFromMetallic(albedo, _Metallic, /*out*/specColor, /*out*/oneMinusReflectivity);UnityMetaInput o;UNITY_INITIALIZE_OUTPUT(UnityMetaInput, o);o.Albedo = GetLightmappingAlbedo(diffColor, specColor, _Glossiness);o.SpecularColor = specColor;o.Emission = GetEmission(i);return UnityMetaFragment(o);
}#endif

3.2 MetaPass 顶点着色器与片段着色器

Meta 只有一个目的,那就是计算出反射率(Albdeo)、高光(SpecularColor)和自发光(emissive)三个参数,将它们传入内置的 UnityMetaInput 结构中由片段着色器返回

片段着色器中用了一个内置方法 DiffuseAndSpecularFromMetallic,我们只需要传入输入的反射率和金属度,就可以同时得到漫反射光和高光,毕竟在主 Pass 中肯定已经写过一遍了,这里就可以做简化。不过光照贴图的反照率是漫反射颜色,但是粗糙的金属仍然会在周围散射相当多的光(尽管它的漫反射颜色是黑色的),所以这里最好也要考虑这一点

//光照贴图的反照率应该就是漫反射颜色,但是粗糙的金属仍然会在周围散射相当多的光(尽管它的漫反射颜色是黑色的),所以这里最好也要考虑这一点
half3 GetLightmappingAlbedo(half3 diffuse, half3 specular, half smoothness)
{half roughness = 1 - smoothness;roughness *= roughness;half3 res = diffuse;res += specular * roughness * 0.5;return res;
}float4 frag_meta(v2f_meta i): SV_Target
{half3 albedo = GetAlbedo(i);half oneMinusReflectivity;half3 specColor;half3 diffColor = DiffuseAndSpecularFromMetallic(albedo, _Metallic, /*out*/specColor, /*out*/oneMinusReflectivity);UnityMetaInput o;UNITY_INITIALIZE_OUTPUT(UnityMetaInput, o);o.Albedo = GetLightmappingAlbedo(diffColor, specColor, _Glossiness);o.SpecularColor = specColor;o.Emission = GetEmission(i);return UnityMetaFragment(o);
}

顶点着色器使用了 Unity 内置方法 UnityMetaVertexPosition 去计算世界坐标点,它的内部实现如下:

float4 UnityMetaVertexPosition(float4 vertex, float2 uv1, float2 uv2, float4 lightmapST, float4 dynlightmapST)
{#if !defined(EDITOR_VISUALIZATION)//使用静态光照if (unity_MetaVertexControl.x){vertex.xy = uv1 * lightmapST.xy + lightmapST.zw;// OpenGL right now needs to actually use incoming vertex position,// so use it in a very dummy wayvertex.z = vertex.z > 0 ? 1.0e-4f : 0.0f;}//使用动态光照if (unity_MetaVertexControl.y){vertex.xy = uv2 * dynlightmapST.xy + dynlightmapST.zw;// OpenGL right now needs to actually use incoming vertex position,// so use it in a very dummy wayvertex.z = vertex.z > 0 ? 1.0e-4f : 0.0f;}return mul(UNITY_MATRIX_VP, float4(vertex.xyz, 1.0));#elsereturn UnityObjectToClipPos(vertex);#endif
}

方法内部看上去不是很好理解,特别是顶点位置的映射。因为它是为了提供数据给光照烘焙系统,而不是摄像机,所以这里相当于是要将坐标映射到光照贴图上的位置

如果没问题的话,就可以完全用自己的 Shader 得到一个正确的带有间接光的场景了:

3.3 自发光物体

如果片段着色器中计算出的反射率(Albdeo)是有值的,那么就可以看到周围物体间接光的效果,但如果物体本身拥有较强的自发光(emissive),就有可能得不到正确的效果:自发光对间接光的贡献为0

这个问题出在 Shader 的 globalIlluminationFlags 属性上,它用于存储材质与光照贴图和光照探针的交互方式,需要在 ShaderGUI 中将这个属性暴露:

void DoubleGI(MaterialEditor materialEditor)
{EditorGUI.BeginChangeCheck();m_MaterialEditor.LightmapEmissionProperty();if (EditorGUI.EndChangeCheck()){Material m_Material = materialEditor.target as Material;m_Material.globalIlluminationFlags &= ~MaterialGlobalIlluminationFlags.EmissiveIsBlack}m_MaterialEditor.DoubleSidedGIField();
}

可以看到它有三个选项:分别对应着官方文档中的这三个变量:

  • None:发射光照完全不影响全局光照
  • RealtimeEmissive:发射光照会影响实时全局光照,它将光照发射到实时光照贴图和实时光照探针中
  • BakedEmissive:发射光照影响烘焙全局光照,它将光照发射到烘焙光照贴图和烘焙光照探针中

由于物体拥有自发光属性就意味着它是作为光源的,因此这里要设置为 BakedEmissive,但是还有问题,globalIlluminationFlags 还有第四个变量,也就是 EmissiveIsBlack,它为 true 意味着发射光照保证是黑色的,这样光照贴图系统可知道不必从材质中提取发射光照信息。可是它在材质界面并没有被暴露,所以还需要做一个小操作,将它在代码里置为 false,这样问题就解决了

四、Directional Lightmaps

由于场景中的静态物体不带直接光照数据,基本上辐射率都来源于采样 lightmaps,其次是镜面 IBL,因此对于有法线贴图的物体,可能无法得到正确的结果:你会发现法线贴图几乎不起作用。这很正常,光照烘焙系统也只使用几何体的顶点数据

应当正确的结果

在 Lightmapping 设置里面,有一个 Directional Mode 选项,之前的例子里默认的是 Non-Directional:

  • Non-Directional:不会生成额外的 Directional Lightmaps
  • Directional:Unity 会额外生成第二张 Lightmap 来存储入射光的主要方向,在一些很旧的平台上(例如 GLES 2.0)可能不支持

这次改成 Directional 重新烘焙,就可以看到额外的 lightmap 了:

接下来是 Shader:

对于 lightmap 有一个很关键的宏 LIGHTMAP_ON,而 Directional lighmap 一样有,那就是 DIRLIGHTMAP_COMBINED 和 DIRLIGHTMAP_OFF,如果觉得加一些 #pragma shader_feature 预编译指令比较麻烦,可以直接通过下面一行代码全部包括,它帮你考虑了前向渲染中所以可能用到的 keyword,不过这样可能会导致变体变多

//#pragma multi_compile __ LIGHTMAP_ON VERTEXLIGHT_ON    //不再需要了
#pragma multi_compile_fwdbase

接下来就是采样部分:

#if defined(LIGHTMAP_ON)float3 indirectDiffuse = DecodeLightmap(UNITY_SAMPLE_TEX2D(unity_Lightmap, i.lightmapUV));#if defined(DIRLIGHTMAP_COMBINED)float4 lightmapDirection = UNITY_SAMPLE_TEX2D_SAMPLER(unity_LightmapInd, unity_Lightmap, i.lightmapUV);indirectDiffuse = DecodeDirectionalLightmap(indirectDiffuse, lightmapDirection, normal);#endif
#elsefloat3 indirectDiffuse = max(0, ShadeSH9(float4(normal, 1)));
#endif

这里使用了一个小技巧:那就是对于 unity_Lightmap 和 unity_LightmapInd(directional lighmap) 使用了同一个采样器,采样器的数量是有上限的,DX11为16个,对于大型项目而言,如果做不到采样器复用就有可能会超数量:

  • UNITY_DECLARE_TEX2D(tex):定义纹理(Texture2D)和采样器(sampler)
  • UNITY_DECLARE_TEX2D_NOSAMPLER(tex):仅定义纹理
  • UNITY_SAMPLE_TEX2D(tex, coord):纹理采样
  • UNITY_SAMPLE_TEX2D_SAMPLER(tex, samplertex, coord):复用其它纹理的采样器对当前纹理采样

使用相同环绕方式和纹理过滤方式的两张同维度纹理可以使用同一个纹理采样器

其实对于法线纹理和辐照度纹理,也可以使用同一个采样器,之前我们都是这样写的:

sampler2D _MainTex;
sampler2D _EmissionX;
sampler2D _NormalMap;
//纹理3,纹理4……

如果你的平台支持采样器和纹理分离的话,它就同等与下面:

Texture2D _MainTex; sampler2D _MainTex;
Texture2D _EmissionX; sampler2D _EmissionX;
Texture2D _EmissionX; sampler2D _NormalMap;
// 定义纹理与对应的采样器
#define UNITY_DECLARE_TEX2D(tex) Texture2D tex; SamplerState sampler##tex
// 只定义纹理
#define UNITY_DECLARE_TEX2D_NOSAMPLER(tex) Texture2D tex
// 普通的纹理采样
#define UNITY_SAMPLE_TEX2D(tex, coord) tex.Sample(sampler##tex, coord)// 通过samplertex传入其他纹理的名称,可复用其他纹理的采样器进行采样
#define UNITY_SAMPLE_TEX2D_SAMPLER(tex, samplertex, coord) tex.Sample(sampler##samplertex, coord)

和 lightmap 一样,最后需要用 DecodeDirectionalLightmap 方法解码

参考文章:

  • https://blog.csdn.net/wodownload2/article/details/107020406
  • https://catlikecoding.com/unity/tutorials/rendering/part-16/
  • https://blog.csdn.net/cgy56191948/article/details/100766303

UnityGI2:Lightmaps相关推荐

  1. UnityGI5:实时 GI 与光探代理体

    前置:UnityGI2:Lightmaps 一.实时 GI 字面意思,实时计算间接光照,设置与代码修改如下: 1. LightSetting 里面开启 Realtime Lighting,并将对应的主 ...

  2. UnityGI4:混合光照

    前置:UnityGI2:Lightmaps 一.混合模式 前面设置了光源的模式为 Baked,这意味着光源产生的直接光和间接光都会被记录在 lightmap 中,如果着色器不从 lightmap 中采 ...

  3. (一)unity自带的着色器源码剖析之——————UnityShaderVariables.cginc文件

    unityShaderVariables.cginc文件中包含大量的工具宏和函数,如变换操作用的矩阵.与摄像机相关的函数.与光照和阴影相关的函数,以及与雾效果相关的函数等.下面依次分析这些工具函数和宏 ...

  4. 大数据技术之_19_Spark学习_07_Spark 性能调优 + 数据倾斜调优 + 运行资源调优 + 程序开发调优 + Shuffle 调优 + GC 调优 + Spark 企业应用案例

    大数据技术之_19_Spark学习_07 第1章 Spark 性能优化 1.1 调优基本原则 1.1.1 基本概念和原则 1.1.2 性能监控方式 1.1.3 调优要点 1.2 数据倾斜优化 1.2. ...

  5. golang大厂面试2

    golang大厂面试 滴滴 写个二分查找 以下是一个简单的二分查找算法的 Go 语言实现: package mainimport "fmt"// 二分查找函数 func binar ...

  6. InstallShield内部库函数

    InstallShield内部库函数 下载资源:点击 1  库函数综述 InstallShield包含300多个内部库函数,用户可在安装脚本中调用它们来创建程序组,操作文件夹,处理目录,监督安装状态, ...

  7. UnityGI1:光照烘培

    一.全局光照(Global Illumination, GI)系统 全局光照(GI)系统这个概念指的是:既要考虑场景中来自光源的直接光照,又要考虑经光在其他物体表面反射后的间接光照 光线追踪效果图,可 ...

  8. UnityShader15:前向渲染

    一.延迟着色和前向渲染 很可惜的是没有什么前置,OpenGL 本是要写一篇延迟着色的笔记的,但是怎么看这都不属于OpenGL基础的范畴 先考虑最简单的情况:只有最多一个光源,这个时候当然就按照正常渲染 ...

  9. GPU大百科全书 最终章:33毫秒的咏叹调

    视觉的颂歌 前言:GPU大百科的连载已经持续了6个月了,6个月前我们向你承诺过,虽然这是一本不算厚,不算旧,并未囊括古今天下之事,甚至还可能充满了各种谬误的"大百科全书".但是我们 ...

最新文章

  1. Redis键命令(查找键、判断键值是否存在、查看键值类型、删除键值、设置过期时间、查看键值有效时间)
  2. Hexo+腾讯云COS,为你的站点加速
  3. python dict下标_Python基础教程:python的数据类型
  4. php2018面试题20块,php最新面试题2018届毕业生专享
  5. BAT3四大巨头安全负责人破天荒聚在了一起,他们都说了什么?
  6. optee的Share Memory介绍
  7. Android实现退出提示的功能
  8. 用按钮控制游戏物件的开启及关闭
  9. 数据结构与算法:单链表(利用万能指针实现对任意类型数据进行操作)
  10. 2021年五月下旬推荐文章
  11. VC中借助DirectDraw实现水波的模拟
  12. tp3.2 多字段模糊查询
  13. 服务器v1v2v3v4性能区别,昂达平板电脑V1V2V3V4V5版本之间的区别
  14. 手机麦克风声音太大_手机麦克风没声音怎么设置?瞬间声音变大,一键设置即可...
  15. 佛系 vue -01
  16. safari浏览网页打开速度很慢怎样解决
  17. js编程中常用术语-中英对照
  18. 替代MP9486A 输入120V降压恒压IC方案 GPS防盗器IC方案
  19. Mongodb修改器
  20. 大学生HTML5竞赛网站,2019全国大学生信息安全竞赛Web Writeup

热门文章

  1. python免费网课-Python网课推荐——免费学习Python编程
  2. python是什么课程-请问自学 Python 有必要买课程吗?
  3. python工资一般多少p-为什么这么多人喜欢Python?Python的就业方向是什么?
  4. python入门经典100例-【python】编程语言入门经典100例--14
  5. supercharge快充_电荷泵?双电芯?高压低流?盘点目前最全快充技术
  6. 本地apk安装是什么意思_Sony电视安装第三方播放器
  7. 保留五天的日志 php,怎样让日志在归档目录保留5天?
  8. 【Mybatis笔记】mybatis实现mysql增删改查
  9. Vscode 调试:跟踪局部变量的变化
  10. X264 输出的统计值的含义(X264 Stats Output)