一种简易的球面图包裹物体AO实现

最近翻到了一篇2005年siggraph的文章:Ambient Occlusion Fields Janne Kontkanen

https://files-cdn.cnblogs.com/files/hont/kontkanen2005i3d_paper.zip

 

感觉大概思路是通过球体去拟合包裹范围内的模型,得到一个近似点计算AO。

原文方法比较复杂,用了2个Cubemap7个参数。

这里先实现一个简单的,以后有需求再研究,用到了之前CapsuleAO的代码:

https://www.cnblogs.com/hont/p/14965382.html

 

主要使用环境贴图包裹物体,拿到最近点位置计算AO,效果如下:

一种简易的球面图包裹物体AO实现

一种简易的球面图包裹物体AO实现

 

第一步使用全景图绘制一圈射线,然后检测最近距离。

一种简易的球面图包裹物体AO实现

 

第二步生成全景图,交给Shader部分处理。

 

代码如下,CSharp部分:

一种简易的球面图包裹物体AO实现
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class AOFields : MonoBehaviour
{
    [Range(0.01f, 0.1f)] public float xPrecise = 0.1f;//相对全景图x轴的射线精度
    [Range(0.01f, 0.1f)] public float yPrecise = 0.1f;//相对全景图y轴的射线精度
    public float radius = 1f;

    public float[] r;//烘焙的最近距离信息


    private void OnDrawGizmos()
    {
        int k = 0;
        for (float x = 0; x < 1f; x += xPrecise)
        {
            for (float y = 0; y < 1f; y += yPrecise)
            {
                float rEnhance = k >= r.Length ? 1f : r[k];
                Vector3 point3D = EqMapToDir(new Vector2(x, y)) * radius * rEnhance;
                Gizmos.DrawLine(point3D, point3D + point3D.normalized * 0.1f);
                k++;
            }
        }
    }

    private Vector3 EqMapToDir(Vector2 uv)
    {
        float v = 1f - uv.y;
        float theta = v * Mathf.PI;

        float u = uv.x;
        float phi = u * 2f * Mathf.PI;

        float x = Mathf.Sin(phi) * Mathf.Sin(theta) * -1;
        float y = Mathf.Cos(theta);
        float z = Mathf.Cos(phi) * Mathf.Sin(theta) * -1;

        return new Vector3(x, y, z);
    }

    [ContextMenu("Calc R")]
    private void CalcR()//烘焙距离
    {
        r = new float[Mathf.CeilToInt(1f / yPrecise) * Mathf.CeilToInt(1f / xPrecise)];

        int k = 0;
        for (float x = 0; x < 1f; x += xPrecise)
        {
            for (float y = 0; y < 1f; y += yPrecise)
            {
                Vector3 point3D = EqMapToDir(new Vector2(x, y)) * radius;

                if (Physics.Raycast(point3D, -point3D.normalized, out RaycastHit hit))
                {
                    r[k] = hit.point.magnitude / radius;//这里的计算其实不正确
                }

                k++;
            }
        }
    }

    [ContextMenu("Bake Texture")]
    private void BakeTexture()//烘焙成全景图
    {
        int width = Mathf.CeilToInt(1f / xPrecise);
        int height = Mathf.CeilToInt(1f / yPrecise);
        Texture2D tex = new Texture2D(width, height, TextureFormat.ARGB32, true);

        int k = 0;
        for (int x = 0; x < width; x++)
        {
            for (int y = 0; y < height; y++)
            {
                float rEnhance = k >= r.Length ? 1f : r[k];
                tex.SetPixel(x, y, new Color(rEnhance, rEnhance, rEnhance, 1f));
                k++;
            }
        }

        tex.Apply();

#if UNITY_EDITOR
        System.IO.File.WriteAllBytes(System.IO.Directory.GetCurrentDirectory() + "/Assets/AOField.png", tex.EncodeToPNG());
        UnityEditor.AssetDatabase.Refresh();
#endif
    }
}
AOFields.cs

 

传入测试平面位置数据:

一种简易的球面图包裹物体AO实现
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class AOFieldsTestPlane : MonoBehaviour
{
    public MeshRenderer meshRenderer;
    public Transform obstacleTransform;


    private void Update()
    {
        meshRenderer.sharedMaterial.SetVector("_ObstaclePos", obstacleTransform.position);
    }
}
AOFieldsTestPlane.cs

 

Shader部分:

一种简易的球面图包裹物体AO实现
Shader "Unlit/AOFields"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
        _AOFieldTex("AO Field Tex", 2D) = "white"{}
        _AOFieldScale("AO Field Scale",float) = 1.0//缩放
        _AOLimitValue("AO Limit Value", float) = 0.5//最大AO限制
    }
    SubShader
    {
        Tags
        {
            "RenderType"="Opaque"
        }
        
        CULL OFF

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
                float3 normal : NORMAL;
            };

            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
                float3 wPos : TEXCOORD1;
                float3 wNormal : TEXCOORD2;
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;

            sampler2D _AOFieldTex;
            float _AOFieldScale;

            float _AOLimitValue;

            uniform float3 _ObstaclePos;

            
            float2 DirToEqMap(float3 a_coords)
            {
                float3 a_coords_n = normalize(a_coords);
                float lon = atan2(a_coords_n.z, a_coords_n.x);
                float lat = acos(a_coords_n.y);
                float2 sphereCoords = float2(lon, lat) * (1.0 / UNITY_PI);
                return float2( (sphereCoords.x * 0.5 + 0.5), 1 - sphereCoords.y);
            }

            //一个简单粗暴的AO实现
            float Occlusion(float3 pos, float3 nor, float3 sph)
            {
                float3 di = pos - sph;
                float3 diNom = normalize(di);
                float2 uv = DirToEqMap(diNom);
                float intensity = tex2D(_AOFieldTex, uv).r;
                float3 cPoint = sph + diNom * intensity * _AOFieldScale;//得到球体内模型的具体距离点
                return smoothstep(0.0, 1.0, distance(pos,cPoint) / _AOLimitValue);
            }

            v2f vert(appdata v)
            {
                v2f o = (v2f)0;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                o.wPos = mul(unity_ObjectToWorld, v.vertex).xyz;
                o.wNormal = mul((float3x3)UNITY_MATRIX_M, v.normal);
                return o;
            }

            fixed4 frag(v2f i) : SV_Target
            {
                float occ0 = Occlusion(i.wPos, i.wNormal, _ObstaclePos);
                return float4(occ0,occ0,occ0, 1.0);
            }
            ENDCG
        }
    }
}
AOFields.shader

 

上一篇:C深刨_符号_完结篇 【全网第二 负数取模/取余深度刨析!!!】


下一篇:学习python的第三天