{"id":230,"date":"2024-09-27T15:06:49","date_gmt":"2024-09-27T07:06:49","guid":{"rendered":"http:\/\/chenglixue.top\/?p=230"},"modified":"2024-10-06T15:57:45","modified_gmt":"2024-10-06T07:57:45","slug":"unity-%e8%87%aa%e5%ae%9a%e4%b9%89cascade-shadow","status":"publish","type":"post","link":"http:\/\/chenglixue.top\/?p=230","title":{"rendered":"unity \u81ea\u5b9a\u4e49Cascade Shadow"},"content":{"rendered":"<p><div class=\"has-toc have-toc\"><\/div><\/p>\n<p>\u57fa\u7840\u7684shadow map\u5206\u4e3a\u4e24\u4e2a\u6b65\u9aa4\uff1a<\/p>\n<ol>\n<li>\u5728\u5149\u6e90\u5904\u653e\u7f6e\u4e00\u4e2a\u5149\u6e90\u76f8\u673a\uff0c\u4e14\u671d\u5411\u548c\u4f4d\u7f6e\u4e0e\u5149\u6e90\u76f8\u540c\uff0c\u4ee5\u5149\u6e90\u76f8\u673a\u4e3a\u89c6\u89d2\u91c7\u6837\u573a\u666f\u6df1\u5ea6\uff1a<span class=\"katex math inline\">Z_l<\/span><\/li>\n<li>\u56de\u5230\u89d2\u8272\u76f8\u673a\uff0c\u4ee5\u89d2\u8272\u76f8\u673a\u770b\u5411\u573a\u666f\uff0c\u518d\u5c06\u5f53\u524d\u89c6\u89d2\u4e0b\u7684\u7269\u4f53\u7684world position\u8f6c\u6362\u5230\u5149\u6e90\u76f8\u673a\u7684\u89c6\u89d2\u4e0b\uff0c\u8ba1\u7b97\u6df1\u5ea6<span class=\"katex math inline\">Z_{main}<\/span>\u3002\u4e0e<span class=\"katex math inline\">Z_l<\/span>\u6bd4\u8f83\uff0c\u82e5<span class=\"katex math inline\">Z_{main}<\/span> > <span class=\"katex math inline\">Z_l<\/span>\uff0c\u8bf4\u660e\u8be5\u70b9\u5904\u4e8e\u9634\u5f71\uff0c\u5426\u5219\u4e0d\u5728<\/li>\n<\/ol>\n<h1>Shadow Cast<\/h1>\n<p>\u5176\u4e2dShadow Cast\u5373\u4e3a\u4e0a\u8ff0\u7b2c\u4e00\u4e2a\u6b65\u9aa4<\/p>\n<pre><code class=\"line-numbers\">inline float4 EncodeFloatRGBA(float v)\n{\n    float4 enc = float4(1.0, 255.0, 65025.0, 16581375.0) * v;\n    enc = frac(enc);\n    enc -= enc.yzww * float4(1.0\/255.0,1.0\/255.0,1.0\/255.0,0.0);\n\n    return enc;\n}\n\nPSOutput ShadowCast(PSInput i)\n{\n    PSOutput o = (PSOutput)0;\n\n    float depth = i.positionCS.z \/ i.positionCS.w;\n    #if defined (SHADER_TARGET_GLSL)\n        depth = depth * 0.5f + 0.5f;\n    #elif defined(UNITY_REVERSED_Z)\n        depth = 1.f - depth;\n    #endif\n\n    o.color = EncodeFloatRGBA(depth);\n\n    return o;\n}\n<\/code><\/pre>\n<h1>Cascade Shadow<\/h1>\n<p>\u57fa\u672c\u7684shadow mapping\u5b58\u5728\u4e00\u5b9a\u7684\u95ee\u9898\uff1a\u5f15\u64ce\u4e2d\u5bf9\u7eb9\u7406\u662f\u81ea\u52a8\u63d0\u4f9bMipmap\uff0c\u800c\u9634\u5f71\u662f\u6ca1\u6709\u7684\uff0c\u8fd9\u5c31\u5bfc\u81f4\u76f8\u673a\u79bb\u4e0d\u7ba1\u8fdc\u8fd1\uff0c\u91c7\u6837\u7684shadow map\u7684\u5206\u8fa8\u7387\u662f\u76f8\u540c\u7684\u3002\u82e5shadow map\u7684\u5206\u8fa8\u7387\u8fc7\u9ad8\uff0c\u91c7\u6837\u7684\u6027\u80fd\u6d88\u8017\u662f\u4e0d\u80fd\u63a5\u53d7\u7684\uff1b\u4f46shadow map\u7684\u5206\u8fa8\u7387\u8fc7\u4f4e\uff0c\u9634\u5f71\u53c8\u4f1a\u6709\u952f\u9f7f<\/p>\n<p>\u7ea7\u8054\u9634\u5f71\u8d34\u56fe(Cascaded Shadow Mapping)\u53ef\u4ee5\u89e3\u51b3\u8fd9\u4e2a\u95ee\u9898\uff0c\u5b83\u4e3a\u4e0d\u540c\u8ddd\u79bb\u7684\u7269\u4f53\u7ed8\u5236\u4e0d\u540c\u5206\u8fa8\u7387\u7684\u9634\u5f71\u8d34\u56fe\u3002\u5bf9\u4e8e\u900f\u89c6\u76f8\u673a\uff0c\u8fd1\u5904\u7684\u7269\u4f53\u5360\u636e\u5c4f\u5e55\u7684\u5927\u90e8\u5206\u533a\u57df\u6240\u4ee5\u7406\u5e94\u5e94\u6709\u5206\u8fa8\u7387\u66f4\u9ad8\u7684\u9634\u5f71\u8d34\u56fe\uff1b\u800c\u8fdc\u5904\u7684\u7269\u4f53\u4f7f\u7528\u5206\u8fa8\u7387\u66f4\u4f4e\u7684\u9634\u5f71\u8d34\u56fe<\/p>\n<p>Cascaded Shadow Mapping\u7684<strong>\u6838\u5fc3\u5728\u4e8e\u628a\u5149\u6e90\u76f8\u673a\u7684\u89c6\u9525\u4f53(AABB)\u8fdb\u884c\u591a\u5c42\u5206\u7ea7\uff0c\u4ece\u8fd1\u5230\u8fdc\uff0c\u6bcf\u5c42\u89c6\u9525\u4f53\u9010\u6e10\u53d8\u5927<\/strong>\u3002\u5176\u4e2d\u7684\u96be\u70b9\u5728\u4e8e\u5982\u4f55\u6784\u9020AABB<\/p>\n<ol>\n<li>\u521d\u59cb\u5316\u4e3b\u76f8\u673a\u548c\u5149\u6e90\u76f8\u673a\u7684\u89c6\u9525\u4f53\u8fd1\u8fdc\u5e73\u9762\u7684\u516b\u4e2a\u9876\u70b9\n<pre><code class=\"line-numbers\">\/\/\/ &lt;summary&gt;\n\/\/\/ \u8fd1\u8fdc\u5e73\u9762\u7684\u56db\u4e2a\u9876\u70b9\u5750\u6807\n\/\/\/ &lt;\/summary&gt;\nstruct FrustumCorners\n{\n   public Vector3[] nearCorners;\n   public Vector3[] farCorners;\n}\n\nvoid InitFrustumCorners()\n{\n   _mainCameraFrustumCorners = new FrustumCorners[4];\n   _dirLightCameraFrustumCorners = new FrustumCorners[4];\n\n   for (int i = 0; i &lt; 4; ++i)\n   {\n       _mainCameraFrustumCorners[i].nearCorners = new Vector3[4];\n       _mainCameraFrustumCorners[i].farCorners = new Vector3[4];\n       _dirLightCameraFrustumCorners[i].nearCorners = new Vector3[4];\n       _dirLightCameraFrustumCorners[i].farCorners = new Vector3[4];\n   }\n}\n<\/code><\/pre>\n<\/li>\n<li>\u8ba1\u7b97\u4e3b\u76f8\u673a\u7684\u89c6\u9525\u4f53\u8fd1\u8fdc\u5e73\u9762\u7684\u516b\u4e2a\u9876\u70b9\u5728world space\u4e0b\u7684\u5750\u6807\u503c\n<pre><code class=\"line-numbers\">private static Vector4 _CSMSplitRatio = new Vector4(0.067f, 0.133f, 0.267f, 0.533f);\n\nvoid CalcMainCameraFrustumCorners()\n{\n   float near = _mainCamera.nearClipPlane;\n   float far = _mainCamera.farClipPlane;\n\n   float[] nears = \n   { \n       near, \n       near + far * _CSMSplitRatio[0],\n       near + far * (_CSMSplitRatio[0] + _CSMSplitRatio[1]), \n       near + far * (_CSMSplitRatio[0] + _CSMSplitRatio[1] + _CSMSplitRatio[2])\n\n   };\n   float[] fars =\n   {\n       near + far * _CSMSplitRatio[0],\n       near + far * (_CSMSplitRatio[0] + _CSMSplitRatio[1]),\n       near + far * (_CSMSplitRatio[0] + _CSMSplitRatio[1] + _CSMSplitRatio[2]),\n       far\n   };\n\n   _CSMSplitNear = nears;\n   _CSMSplitFar = fars;\n   Shader.SetGlobalVector(\"_G_CSM_Split_Nears\", new Vector4(_CSMSplitNear[0], _CSMSplitNear[1], _CSMSplitNear[2], _CSMSplitNear[3]));\n   Shader.SetGlobalVector(\"_G_CSM_Split_Fars\", new Vector4(_CSMSplitFar[0], _CSMSplitFar[1], _CSMSplitFar[2], _CSMSplitFar[3]));\n\n   for (int j = 0; j &lt; 4; ++j)\n   {\n       \/\/ \u8ba1\u7b97local space\u4e0b\u76f8\u673a\u8fd1\u5e73\u9762\u7684\u56db\u4e2a\u9876\u70b9\n       _mainCamera.CalculateFrustumCorners(new Rect(0, 0, 1, 1), _CSMSplitNear[j], Camera.MonoOrStereoscopicEye.Mono, _mainCameraFrustumCorners[j].nearCorners);\n       _mainCamera.CalculateFrustumCorners(new Rect(0, 0, 1, 1), _CSMSplitFar[j], Camera.MonoOrStereoscopicEye.Mono, _mainCameraFrustumCorners[j].farCorners);\n\n       for (int i = 0; i &lt; 4; ++i)\n       {\n           \/\/ \u5c06local space\u4e0b\u76f8\u673a\u8fd1\u5e73\u9762\u7684\u56db\u4e2a\u9876\u70b9\u8f6c\u6362\u5230world space\n           _mainCameraFrustumCorners[j].nearCorners[i] = _mainCamera.transform.TransformPoint(_mainCameraFrustumCorners[j].nearCorners[i]);\n           _mainCameraFrustumCorners[j].farCorners[i]  = _mainCamera.transform.TransformPoint(_mainCameraFrustumCorners[j].farCorners[i]);\n       }\n   }\n}\n<\/code><\/pre>\n<\/li>\n<li>\u5c06\u4e3b\u76f8\u673a\u7684\u89c6\u9525\u4f53\u53d8\u6362\u5230\u5149\u6e90\u7a7a\u95f4\uff0c\u5e76\u8ba1\u7b97\u5149\u6e90\u76f8\u673a\u7684AABB\n<pre><code class=\"line-numbers\">void CalcLightCameraFrustumCorners()\n{\n   if (_dirLightCamera == null) return;\n\n   for (int j = 0; j &lt; 4; ++j)\n   {\n       for (int i = 0; i &lt; 4; ++i)\n       {\n           _dirLightCameraFrustumCorners[j].nearCorners[i] = _dirLightCamerasData[j].transform.InverseTransformPoint(_mainCameraFrustumCorners[j].nearCorners[i]);\n           _dirLightCameraFrustumCorners[j].farCorners[i]  = _dirLightCamerasData[j].transform.InverseTransformPoint(_mainCameraFrustumCorners[j].farCorners[i]);\n       }\n\n       float[] corners_x =\n       {\n           _dirLightCameraFrustumCorners[j].nearCorners[0].x, _dirLightCameraFrustumCorners[j].nearCorners[1].x,\n           _dirLightCameraFrustumCorners[j].nearCorners[2].x, _dirLightCameraFrustumCorners[j].nearCorners[3].x,\n           _dirLightCameraFrustumCorners[j].farCorners[0].x, _dirLightCameraFrustumCorners[j].farCorners[1].x,\n           _dirLightCameraFrustumCorners[j].farCorners[2].x, _dirLightCameraFrustumCorners[j].farCorners[3].x\n       };\n       float[] corners_y =\n       {\n           _dirLightCameraFrustumCorners[j].nearCorners[0].y, _dirLightCameraFrustumCorners[j].nearCorners[1].y,\n           _dirLightCameraFrustumCorners[j].nearCorners[2].y, _dirLightCameraFrustumCorners[j].nearCorners[3].y,\n           _dirLightCameraFrustumCorners[j].farCorners[0].y, _dirLightCameraFrustumCorners[j].farCorners[1].y,\n           _dirLightCameraFrustumCorners[j].farCorners[2].y, _dirLightCameraFrustumCorners[j].farCorners[3].y\n       };\n       float[] corners_z =\n       {\n           _dirLightCameraFrustumCorners[j].nearCorners[0].z, _dirLightCameraFrustumCorners[j].nearCorners[1].z,\n           _dirLightCameraFrustumCorners[j].nearCorners[2].z, _dirLightCameraFrustumCorners[j].nearCorners[3].z,\n           _dirLightCameraFrustumCorners[j].farCorners[0].z, _dirLightCameraFrustumCorners[j].farCorners[1].z,\n           _dirLightCameraFrustumCorners[j].farCorners[2].z, _dirLightCameraFrustumCorners[j].farCorners[3].z\n       };\n\n       float min_x = Mathf.Min(corners_x);\n       float max_x = Mathf.Max(corners_x);\n       float min_y = Mathf.Min(corners_y);\n       float max_y = Mathf.Max(corners_y);\n       float min_z = Mathf.Min(corners_z);\n       float max_z = Mathf.Max(corners_z);\n\n       _dirLightCameraFrustumCorners[j].nearCorners[0] = new Vector3(min_x, min_y, min_z);\n       _dirLightCameraFrustumCorners[j].nearCorners[1] = new Vector3(max_x, min_y, min_z);\n       _dirLightCameraFrustumCorners[j].nearCorners[2] = new Vector3(max_x, max_y, min_z);\n       _dirLightCameraFrustumCorners[j].nearCorners[3] = new Vector3(min_x, max_y, min_z);\n\n       _dirLightCameraFrustumCorners[j].farCorners[0] = new Vector3(min_x, min_y, max_z);\n       _dirLightCameraFrustumCorners[j].farCorners[1] = new Vector3(max_x, min_y, max_z);\n       _dirLightCameraFrustumCorners[j].farCorners[2] = new Vector3(max_x, max_y, max_z);\n       _dirLightCameraFrustumCorners[j].farCorners[3] = new Vector3(min_x, max_y, max_z);\n\n       \/\/\/ \u907f\u514d\u79fb\u52a8\u76f8\u673a\u89c6\u89d2\u9020\u6210\u9634\u5f71\u6296\u52a8\n       float width = Vector3.Magnitude(_dirLightCameraFrustumCorners[j].nearCorners[1] - _dirLightCameraFrustumCorners[j].nearCorners[0]);\n       float height = Vector3.Magnitude(_dirLightCameraFrustumCorners[j].nearCorners[2] - _dirLightCameraFrustumCorners[j].nearCorners[1]);\n       float cross = Vector3.Magnitude(_dirLightCameraFrustumCorners[j].farCorners[2] - _dirLightCameraFrustumCorners[j].nearCorners[0]);\n       float maxDis = Mathf.Max(width, height);\n       maxDis = cross;\n       if (!_crossDisCached.TryGetValue(j, out maxDis))\n       {\n           if (cross != 0)\n           {\n               _crossDisCached.Add(j, cross);\n               maxDis = cross;\n           }\n       }\n       else\n       {\n           if (cross &gt; maxDis)\n           {\n               _crossDisCached[j] = cross;\n           }\n       }\n\n       float unitPerPixel = maxDis \/ shadowMapRTs[j].width;\n       Vector3 lightCameraPosOS = _dirLightCameraFrustumCorners[j].nearCorners[0] + (_dirLightCameraFrustumCorners[j].nearCorners[2] - _dirLightCameraFrustumCorners[j].nearCorners[0]) * 0.5f;\n       lightCameraPosOS.x = Mathf.Floor(lightCameraPosOS.x \/ unitPerPixel) * unitPerPixel;\n       lightCameraPosOS.y = Mathf.Floor(lightCameraPosOS.y \/ unitPerPixel) * unitPerPixel;\n\n       _dirLightCamerasData[j].transform.position = _dirLightCamerasData[j].transform.TransformPoint(lightCameraPosOS);\n       _dirLightCamerasData[j].transform.rotation = RenderSettings.sun.transform.rotation;\n   }\n}\n<\/code><\/pre>\n<p>\u5c06AABB\u53ef\u89c6\u5316\u5982\u4e0b\uff1a<br \/>\n<img decoding=\"async\"   class=\"lazyload\" data-src=\"https:\/\/pic.imgdb.cn\/item\/66f539baf21886ccc0ecd5cb.png\" src=\"https:\/\/cdn.jsdelivr.net\/gh\/moezx\/cdn@3.0.2\/img\/svg\/loader\/trans.ajax-spinner-preloader.svg\" onerror=\"imgError(this)\"  alt=\"\" \/><\/p >\n<noscript><img decoding=\"async\" src=\"https:\/\/pic.imgdb.cn\/item\/66f539baf21886ccc0ecd5cb.png\" alt=\"\" \/><\/p><\/noscript>\n<\/li>\n<li>\n<p>\u6784\u5efa\u5149\u6e90\u76f8\u673a\u7684position\u3001rotation\u3001\u8fd1\u8fdc\u5e73\u9762\u3001aspect\u3001orthographicSize<\/p>\n<pre><code class=\"line-numbers\">void CalcDirLightCameraSplits(int index)\n{\n   _dirLightCamera.transform.position = _dirLightCamerasData[index].transform.position;\n   _dirLightCamera.transform.rotation = _dirLightCamerasData[index].transform.rotation;\n\n   _dirLightCamera.nearClipPlane = _dirLightCameraFrustumCorners[index].nearCorners[0].z;\n   _dirLightCamera.farClipPlane  = _dirLightCameraFrustumCorners[index].farCorners[0].z;\n\n   _dirLightCamera.aspect = 1;\n\n   float cross = Vector3.Magnitude(_dirLightCameraFrustumCorners[index].farCorners[2] - _dirLightCameraFrustumCorners[index].nearCorners[0]);\n   float maxDis = -1;\n   if (!_crossDisCached.TryGetValue(index, out maxDis))\n   {\n       if (cross != 0)\n       {\n           _crossDisCached.Add(index, cross);\n           maxDis = cross;\n       }\n   }\n   else\n   {\n       if (cross &gt; maxDis)\n       {\n           _crossDisCached[index] = cross;\n       }\n   }\n\n   _dirLightCamera.orthographicSize = maxDis  * 0.5f;\n}\n<\/code><\/pre>\n<\/li>\n<li>\u6700\u540e\u518d\u8fdb\u884cshadow map\n<pre><code class=\"line-numbers\">\/\/ \u83b7\u5f97cpu\u7aef\u8bbe\u7f6e\u7684\u7ea7\u8054\u7b49\u7ea7\nfloat4 CSMWeights = GetCSMWeights(view_z);\n\nfloat3 normalBias = normalWS * _G_Shadow_NormalBias;\nfloat3 shadowPos0 = TransformWorldToShadow(posWS + normalBias, _G_Matrix_WorldToShadow[0]);\nfloat3 shadowPos1 = TransformWorldToShadow(posWS + normalBias, _G_Matrix_WorldToShadow[1]);\nfloat3 shadowPos2 = TransformWorldToShadow(posWS + normalBias, _G_Matrix_WorldToShadow[2]);\nfloat3 shadowPos3 = TransformWorldToShadow(posWS + normalBias, _G_Matrix_WorldToShadow[3]);\n\nfloat bias = GetSlopeBias(normalWS, lightDirWS);\nfloat depth0 = shadowPos0.z + bias;\nfloat depth1 = shadowPos1.z + bias;\nfloat depth2 = shadowPos2.z + bias;\nfloat depth3 = shadowPos3.z + bias;\n\n\/\/ \u8fdb\u884cshadow map\nfloat shadow0 = PCF_High(shadowPos0.xy, depth0, _G_ShadowMap_Tex0);\nfloat shadow1 = PCF_High(shadowPos1.xy, depth1, _G_ShadowMap_Tex1);\nfloat shadow2 = PCF_High(shadowPos2.xy, depth2, _G_ShadowMap_Tex2);\nfloat shadow3 = PCF_High(shadowPos3.xy, depth3, _G_ShadowMap_Tex3);\nsum = shadow0 * CSMWeights[0] + shadow1 * CSMWeights[1] + shadow2 * CSMWeights[2] + shadow3 * CSMWeights[3];\n<\/code><\/pre>\n<p>\u53ef\u4ee5\u5f97\u5230\u5982\u4e0b\u7ed3\u679c\uff1a<br \/>\n<img decoding=\"async\"   class=\"lazyload\" data-src=\"https:\/\/pic.imgdb.cn\/item\/66f5b035f21886ccc05683e8.png\" src=\"https:\/\/cdn.jsdelivr.net\/gh\/moezx\/cdn@3.0.2\/img\/svg\/loader\/trans.ajax-spinner-preloader.svg\" onerror=\"imgError(this)\"  alt=\"\" \/><\/p >\n<noscript><img decoding=\"async\" src=\"https:\/\/pic.imgdb.cn\/item\/66f5b035f21886ccc05683e8.png\" alt=\"\" \/><\/p><\/noscript>\n<\/li>\n<li>\n<p>Debug\u5177\u4f53\u7684\u7ea7\u8054\u5206\u5e03<\/p>\n<pre><code class=\"line-numbers\">sum *= CSMWeights;\n<\/code><\/pre>\n<p><img decoding=\"async\"   class=\"lazyload\" data-src=\"https:\/\/pic.imgdb.cn\/item\/66f5b080f21886ccc056afbb.png\" src=\"https:\/\/cdn.jsdelivr.net\/gh\/moezx\/cdn@3.0.2\/img\/svg\/loader\/trans.ajax-spinner-preloader.svg\" onerror=\"imgError(this)\"  alt=\"\" \/><\/p >\n<noscript><img decoding=\"async\" src=\"https:\/\/pic.imgdb.cn\/item\/66f5b080f21886ccc056afbb.png\" alt=\"\" \/><\/p><\/noscript>\n<\/li>\n<\/ol>\n","protected":false},"excerpt":{"rendered":"<p>\u57fa\u7840\u7684shadow map\u5206\u4e3a\u4e24\u4e2a\u6b65\u9aa4\uff1a \u5728\u5149\u6e90\u5904\u653e\u7f6e\u4e00\u4e2a\u5149\u6e90\u76f8\u673a\uff0c\u4e14\u671d\u5411\u548c\u4f4d\u7f6e\u4e0e\u5149\u6e90\u76f8\u540c\uff0c\u4ee5\u5149\u6e90\u76f8\u673a\u4e3a\u89c6\u89d2\u91c7\u6837\u573a\u666f\u6df1\u5ea6\uff1aZ_l  &#8230;<\/p>","protected":false},"author":1,"featured_media":239,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[3],"tags":[],"_links":{"self":[{"href":"http:\/\/chenglixue.top\/index.php?rest_route=\/wp\/v2\/posts\/230"}],"collection":[{"href":"http:\/\/chenglixue.top\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/chenglixue.top\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/chenglixue.top\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/chenglixue.top\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=230"}],"version-history":[{"count":2,"href":"http:\/\/chenglixue.top\/index.php?rest_route=\/wp\/v2\/posts\/230\/revisions"}],"predecessor-version":[{"id":245,"href":"http:\/\/chenglixue.top\/index.php?rest_route=\/wp\/v2\/posts\/230\/revisions\/245"}],"wp:featuredmedia":[{"embeddable":true,"href":"http:\/\/chenglixue.top\/index.php?rest_route=\/wp\/v2\/media\/239"}],"wp:attachment":[{"href":"http:\/\/chenglixue.top\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=230"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/chenglixue.top\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=230"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/chenglixue.top\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=230"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}