Пример #1
0
Файл: BNAO.cs Проект: Foda/BNAO
    void Bake(GameObject[] selection)
    {
        if (Application.isPlaying || selection == null || selection.Length < 1)
        {
            return;
        }

        Material dataCacher  = new Material(Shader.Find("Hidden/BNAO_DataCacher"));
        Material postProcess = new Material(Shader.Find("Hidden/BNAO_PostProcess"));
        Material dilate      = new Material(Shader.Find("Hidden/BNAO_Dilate"));
        Material bnao        = new Material(Shader.Find("Hidden/BNAO"));
        Material composite   = new Material(Shader.Find("Hidden/BNAO_Composite"));

        Shader depthShader;

        if (cullOverrideMode == CullOverrideMode.ForceTwoSided)
        {
            depthShader = Shader.Find("Hidden/BNAO_Depth_2Sided");
        }
        else if (cullOverrideMode == CullOverrideMode.ForceOneSided)
        {
            depthShader = Shader.Find("Hidden/BNAO_Depth_1Sided");
        }
        else
        {
            depthShader = Shader.Find("Hidden/BNAO_Depth");
        }

        // Get objects to be rendered
        var bnaoObjects          = new Dictionary <Material, BNAOObject>();
        var meshRenderers        = new List <MeshRenderer>();
        var skinnedMeshRenderers = new List <SkinnedMeshRenderer>();

        foreach (var gameObject in selection)
        {
            meshRenderers.AddRange(gameObject.GetComponentsInChildren <MeshRenderer>());
            skinnedMeshRenderers.AddRange(gameObject.GetComponentsInChildren <SkinnedMeshRenderer>());
        }
        Material mat = null;

        foreach (var renderer in meshRenderers)
        {
            var meshFilter = renderer.GetComponent <MeshFilter>();
            if (meshFilter && meshFilter.sharedMesh)
            {
                if (forceSharedTexture)
                {
                    if (bnaoObjects.Count < 1)
                    {
                        mat = renderer.sharedMaterial;
                        bnaoObjects.Add(mat, new BNAOObject(renderer.name, (int)bakeRes));
                    }
                    bnaoObjects[mat].renderedMeshes.Add(new RendereredMesh(renderer, meshFilter.sharedMesh));
                }
                else
                {
                    if (!bnaoObjects.ContainsKey(renderer.sharedMaterial))
                    {
                        bnaoObjects.Add(renderer.sharedMaterial, new BNAOObject(renderer.name, (int)bakeRes));
                    }
                    bnaoObjects[renderer.sharedMaterial].renderedMeshes.Add(new RendereredMesh(renderer, meshFilter.sharedMesh));
                }
            }
        }
        foreach (var renderer in skinnedMeshRenderers)
        {
            if (renderer.sharedMesh)
            {
                Mesh mesh = new Mesh();
                mesh.name = renderer.sharedMesh.name + "_snapshot";
                renderer.BakeMesh(mesh);

                if (forceSharedTexture)
                {
                    if (bnaoObjects.Count < 1)
                    {
                        mat = renderer.sharedMaterial;
                        bnaoObjects.Add(mat, new BNAOObject(renderer.name, (int)bakeRes));
                    }
                    bnaoObjects[mat].renderedMeshes.Add(new RendereredMesh(renderer, mesh));
                }
                else
                {
                    if (!bnaoObjects.ContainsKey(renderer.sharedMaterial))
                    {
                        bnaoObjects.Add(renderer.sharedMaterial, new BNAOObject(renderer.name, (int)bakeRes));
                    }
                    bnaoObjects[renderer.sharedMaterial].renderedMeshes.Add(new RendereredMesh(renderer, mesh));
                }
            }
        }

        if (bnaoObjects.Count < 1)
        {
            // No renderers found, abort
            return;
        }

        baking = true;

        // Mode switch
        switch (bakeMode)
        {
        case BakeMode.BentNormal:
            Shader.EnableKeyword("MODE_BN");
            Shader.DisableKeyword("MODE_AO");
            break;

        case BakeMode.AmbientOcclusion:
            Shader.DisableKeyword("MODE_BN");
            Shader.EnableKeyword("MODE_AO");
            break;
        }

        // Cache data to textures
        foreach (var bnaoObject in bnaoObjects)
        {
            Graphics.SetRenderTarget(new RenderBuffer[2] {
                bnaoObject.Value.positionCache.colorBuffer, bnaoObject.Value.normalCache.colorBuffer
            }, bnaoObject.Value.positionCache.depthBuffer);
            foreach (var renderMesh in bnaoObject.Value.renderedMeshes)
            {
                if (useNormalMaps && !forceSharedTexture)
                {
                    if (renderMesh.renderer.sharedMaterial.HasProperty("_NormalTex"))
                    {
                        dataCacher.SetTexture("_NormalTex", renderMesh.renderer.sharedMaterial.GetTexture("_NormalTex"));
                        dataCacher.SetFloat("_HasNormalTex", 1);
                    }
                    else if (renderMesh.renderer.sharedMaterial.HasProperty("_BumpMap"))
                    {
                        dataCacher.SetTexture("_NormalTex", renderMesh.renderer.sharedMaterial.GetTexture("_BumpMap"));
                        dataCacher.SetFloat("_HasNormalTex", 1);
                    }
                    else
                    {
                        dataCacher.SetFloat("_HasNormalTex", 0);
                    }
                }
                dataCacher.SetFloat("_UVChannel", (float)uvChannel);
                dataCacher.SetPass(0);
                Graphics.DrawMeshNow(renderMesh.mesh, renderMesh.renderer.transform.localToWorldMatrix);
            }
        }

        // Calculate spherical bounds of all objects to be rendered
        var  bounds = new Bounds();
        bool first  = true;

        foreach (var bnaoObject in bnaoObjects)
        {
            foreach (var rendermesh in bnaoObject.Value.renderedMeshes)
            {
                if (first)
                {
                    bounds = rendermesh.renderer.bounds;
                    first  = false;
                }
                else
                {
                    bounds.Encapsulate(rendermesh.renderer.bounds);
                }
            }
        }

        Vector3 rendererCenter = bounds.center;
        float   rendererRadius = Mathf.Max(Mathf.Max(bounds.extents.x, bounds.extents.y), bounds.extents.z);

        // Initialize shadow map
        var shadowMap = RenderTexture.GetTemporary((int)shadowMapRes, (int)shadowMapRes, 24, RenderTextureFormat.Shadowmap);

        // Initialize camera
        var go     = new GameObject("BNAO_BakeCamera");
        var camera = go.AddComponent <Camera>();

        camera.enabled          = false;
        camera.farClipPlane     = rendererRadius * 2;
        camera.nearClipPlane    = 0.01f;
        camera.orthographic     = true;
        camera.orthographicSize = rendererRadius;
        camera.aspect           = 1f;
        camera.targetTexture    = shadowMap;

        // Disable rest of scene
        var scene        = FindObjectsOfType <Renderer>();
        var sceneEnabled = new bool[scene.Length];

        for (int i = 0; i < scene.Length; i++)
        {
            sceneEnabled[i] = scene[i].enabled;
            if (!includeScene)
            {
                scene[i].enabled = false;
            }
        }

        // Force enable bake objects and force double sided rendering
        var shadowCastingModes = new List <ShadowCastingMode>();

        foreach (var bnaoObject in bnaoObjects)
        {
            foreach (var renderMesh in bnaoObject.Value.renderedMeshes)
            {
                renderMesh.renderer.enabled = true;
                shadowCastingModes.Add(renderMesh.renderer.shadowCastingMode);
                renderMesh.renderer.shadowCastingMode = ShadowCastingMode.TwoSided;
            }
        }

        // Get random vectors (uniformly distributed points on sphere)
        var directions = PointsOnSphere((int)samples);

        for (int sample = 0; sample < (int)samples; sample++)
        {
            EditorUtility.DisplayProgressBar("Baking " + fullName, "Baking sample " + sample + " / " + (int)(samples) + "...", (float)sample / (float)samples);

            var dir = directions[sample];
            // Position the camera
            camera.transform.position = rendererCenter + dir * rendererRadius;
            // Aim the camera
            camera.transform.rotation = Quaternion.LookRotation(-dir);
            // Render the shadow map
            if (useOriginalShaders)
            {
                camera.Render();
            }
            else
            {
                camera.RenderWithShader(depthShader, "RenderType");
            }

            // Bind shadow map
            Shader.SetGlobalTexture("_ShadowMap", shadowMap);

            // Calculate world-to-shadow matrix
            var proj = camera.projectionMatrix;
            if (SystemInfo.usesReversedZBuffer)
            {
                proj.m20 = -proj.m20;
                proj.m21 = -proj.m21;
                proj.m22 = -proj.m22;
                proj.m23 = -proj.m23;
            }
            var view        = camera.worldToCameraMatrix;
            var scaleOffset = Matrix4x4.identity;
            scaleOffset.m00 = scaleOffset.m11 = scaleOffset.m22 = 0.5f;
            scaleOffset.m03 = scaleOffset.m13 = scaleOffset.m23 = 0.5f;
            var worldToShadow = scaleOffset * (proj * view);
            Shader.SetGlobalMatrix("_WorldToShadow", worldToShadow);

            Shader.SetGlobalFloat("_Samples", (float)samples);
            Shader.SetGlobalFloat("_Sample", (float)sample);
            Shader.SetGlobalVector("_Dir", dir);
            Shader.SetGlobalFloat("_ShadowBias", shadowBias);
            Shader.SetGlobalFloat("_ClampToHemisphere", clampToHemisphere ? 1f : 0f);

            foreach (var bnaoObject in bnaoObjects)
            {
                bnao.SetTexture("_PositionCache", bnaoObject.Value.positionCache);
                bnao.SetTexture("_NormalCache", bnaoObject.Value.normalCache);
                var tmp = RenderTexture.GetTemporary(bnaoObject.Value.result.descriptor);
                tmp.filterMode = FilterMode.Point;
                Graphics.Blit(bnaoObject.Value.result, tmp);
                bnao.SetTexture("_PrevTex", tmp);
                Graphics.Blit(bnaoObject.Value.positionCache, bnaoObject.Value.result, bnao, 0);
                RenderTexture.ReleaseTemporary(tmp);
            }
        }

        EditorUtility.DisplayProgressBar("Baking " + fullName, "Post Processing...", 1);

        // Post process
        foreach (var bnaoObject in bnaoObjects)
        {
            var tmp = RenderTexture.GetTemporary(bnaoObject.Value.result.descriptor);
            tmp.filterMode = FilterMode.Point;
            Graphics.SetRenderTarget(tmp);
            postProcess.SetTexture("_MainTex", bnaoObject.Value.result);
            postProcess.SetFloat("_AOBias", aoBias);
            postProcess.SetFloat("_UVChannel", (float)uvChannel);
            postProcess.SetFloat("_NormalsSpace", (float)bentNormalsSpace);
            postProcess.SetPass(0);
            foreach (var renderMesh in bnaoObject.Value.renderedMeshes)
            {
                postProcess.SetMatrix("_WorldToObject", renderMesh.renderer.transform.worldToLocalMatrix);
                Graphics.DrawMeshNow(renderMesh.mesh, renderMesh.renderer.transform.localToWorldMatrix);
            }
            Graphics.Blit(tmp, bnaoObject.Value.result);
            RenderTexture.ReleaseTemporary(tmp);
        }

        // Dilate
        foreach (var bnaoObject in bnaoObjects)
        {
            var tmp = RenderTexture.GetTemporary(bnaoObject.Value.result.descriptor);
            tmp.filterMode = FilterMode.Point;
            for (int i = 0; i < dilation; i++)
            {
                EditorUtility.DisplayProgressBar("Baking", "Dilating...", (float)i / (float)dilation);
                dilate.SetTexture("_MainTex", bnaoObject.Value.result);
                Graphics.Blit(bnaoObject.Value.result, tmp, dilate);
                i++;
                if (i < dilation)
                {
                    dilate.SetTexture("_MainTex", tmp);
                    Graphics.Blit(tmp, bnaoObject.Value.result, dilate);
                }
                else
                {
                    Graphics.Blit(tmp, bnaoObject.Value.result);
                }
            }
            RenderTexture.ReleaseTemporary(tmp);
        }

        // Output
        int u = 0;

        foreach (var bnaoObject in bnaoObjects)
        {
            EditorUtility.DisplayProgressBar("Baking " + fullName, "Saving texture(s)...", (float)u / bnaoObjects.Count);
            var names = new List <string>();
            foreach (var renderedMesh in bnaoObject.Value.renderedMeshes)
            {
                names.Add(renderedMesh.renderer.name);
            }
            switch (nameMode)
            {
            case NameMode.Shortest:
                names = names.OrderBy(x => x.Length).ToList <string>();
                break;

            case NameMode.Longest:
                names = names.OrderByDescending(x => x.Length).ToList <string>();
                break;

            case NameMode.Alphabetical:
                names.Sort();
                break;

            case NameMode.Whatever:
                break;
            }
            RenderTextureToFile(bnaoObject.Value.result, outputPath + "/" + names[0] + "_" + bakeMode.ToString() + ".png", composite);
            u++;
        }

        // Restore shadow casting modes
        u = 0;
        foreach (var bnaoObject in bnaoObjects)
        {
            foreach (var renderMesh in bnaoObject.Value.renderedMeshes)
            {
                renderMesh.renderer.shadowCastingMode = shadowCastingModes[u];
                u++;
            }
        }

        // Re-enable rest of scene
        for (int i = 0; i < scene.Length; i++)
        {
            scene[i].enabled = sceneEnabled[i];
        }

        // Clean up
        RenderTexture.ReleaseTemporary(shadowMap);
        DestroyImmediate(camera.gameObject);

        UnityEditor.AssetDatabase.Refresh();

        EditorUtility.ClearProgressBar();
        baking = false;
    }