diff --git a/.gitattributes b/.gitattributes deleted file mode 100644 index 919059d6..00000000 --- a/.gitattributes +++ /dev/null @@ -1,112 +0,0 @@ -* text=auto - -*.cs diff=csharp text -*.cginc text -*.shader text -*.spriteatlas text eol=lf -*.docx diff=word -*.html text eol=lf -#text=auto 让git自行处理左边匹配的文件使用何种换行符格式,这是默认选项。 -#text eol=lf 对左边匹配的文件统一使用LF换行符格式,如果有文件中出现CRLF将会转换成LF。 -#binary 告诉git这不是文本文件,不应该对其中的换行符进行改变。另外,binary和符号-text -diff是等价的。 -# Denote all files that are truly binary and should not be modified. -*.png binary -*.jpg binary -# Unity LFS YAML -*.anim -text merge=unityyamlmerge diff -*.asset -text merge=unityyamlmerge diff -*.controller -text merge=unityyamlmerge diff -*.mat -text merge=unityyamlmerge diff -*.meta -text merge=unityyamlmerge diff -*.physicMaterial -text merge=unityyamlmerge diff -*.physicsMaterial2D -text merge=unityyamlmerge diff -*.prefab -text merge=unityyamlmerge diff -*.unity -text merge=unityyamlmerge diff -## git-lfs ## -# Unity LFS -*.cubemap filter=lfs diff=lfs merge=lfs -text -*.unitypackage filter=lfs diff=lfs merge=lfs -text -#2D formats -*.[aA][iI] filter=lfs diff=lfs merge=lfs -text -*.[dD][dD][sS] filter=lfs diff=lfs merge=lfs -text -*.[eE][xX][rR] filter=lfs diff=lfs merge=lfs -text -*.[hH][dD][rR] filter=lfs diff=lfs merge=lfs -text -*.[iI][fF][fF] filter=lfs diff=lfs merge=lfs -text -*.[pP][iI][cC][tT] filter=lfs diff=lfs merge=lfs -text -*.[pP][sS][dD] filter=lfs diff=lfs merge=lfs -text -*.[tT][gG][aA] filter=lfs diff=lfs merge=lfs -text -*.[tT][iI][fF] filter=lfs diff=lfs merge=lfs -text -*.[tT][iI][fF][fF] filter=lfs diff=lfs merge=lfs -text - -#3D formats -*.3[dD][mM] filter=lfs diff=lfs merge=lfs -text -*.3[dD][sS] filter=lfs diff=lfs merge=lfs -text -*.[aA][bB][cC] filter=lfs diff=lfs merge=lfs -text -*.[bB][lL][eE][nN][dD] filter=lfs diff=lfs merge=lfs -text -*.[cC]4[dD] filter=lfs diff=lfs merge=lfs -text -*.[cC][oO][lL][lL][aA][dD][aA] filter=lfs diff=lfs merge=lfs -text -*.[dD][aA][eE] filter=lfs diff=lfs merge=lfs -text -*.[dD][xX][fF] filter=lfs diff=lfs merge=lfs -text -*.[fF][bB][xX] filter=lfs diff=lfs merge=lfs -text -*.[jJ][aA][sS] filter=lfs diff=lfs merge=lfs -text -*.[lL][wW][oO] filter=lfs diff=lfs merge=lfs -text -*.[lL][wW][oO]2 filter=lfs diff=lfs merge=lfs -text -*.[lL][wW][sS] filter=lfs diff=lfs merge=lfs -text -*.[lL][xX][oO] filter=lfs diff=lfs merge=lfs -text -*.[mM][aA] filter=lfs diff=lfs merge=lfs -text -*.[mM][aA][xX] filter=lfs diff=lfs merge=lfs -text -*.[mM][bB] filter=lfs diff=lfs merge=lfs -text -*.[oO][bB][jJ] filter=lfs diff=lfs merge=lfs -text -*.[pP][lL][yY] filter=lfs diff=lfs merge=lfs -text -*.[sS][kK][pP] filter=lfs diff=lfs merge=lfs -text -*.[sS][tT][lL] filter=lfs diff=lfs merge=lfs -text -*.[zZ][tT][lL] filter=lfs diff=lfs merge=lfs -text -# Audio formats -*.[aA][iI][fF] filter=lfs diff=lfs merge=lfs -text -*.[aA][iI][fF][fF] filter=lfs diff=lfs merge=lfs -text -*.[bB][aA][nN][kK] filter=lfs diff=lfs merge=lfs -text -*.[iI][tT] filter=lfs diff=lfs merge=lfs -text -*.[mM][oO][dD] filter=lfs diff=lfs merge=lfs -text -*.[mM][pP]3 filter=lfs diff=lfs merge=lfs -text -*.[oO][gG][gG] filter=lfs diff=lfs merge=lfs -text -*.[sS]3[mM] filter=lfs diff=lfs merge=lfs -text -*.[wW][aA][vV] filter=lfs diff=lfs merge=lfs -text -*.[xX][mM] filter=lfs diff=lfs merge=lfs -text -# Video formats -*.[aA][sS][fF] filter=lfs diff=lfs merge=lfs -text -*.[aA][vV][iI] filter=lfs diff=lfs merge=lfs -text -*.[fF][lL][vV] filter=lfs diff=lfs merge=lfs -text -*.[mM][oO][vV] filter=lfs diff=lfs merge=lfs -text -*.[mM][pP]4 filter=lfs diff=lfs merge=lfs -text -*.[mM][pP][eE][gG] filter=lfs diff=lfs merge=lfs -text -*.[mM][pP][gG] filter=lfs diff=lfs merge=lfs -text -*.[oO][gG][vV] filter=lfs diff=lfs merge=lfs -text -*.[wW][mM][vV] filter=lfs diff=lfs merge=lfs -text -# Build -*.[dD][lL][lL] filter=lfs diff=lfs merge=lfs -text -*.[mM][dD][bB] filter=lfs diff=lfs merge=lfs -text -*.[pP][dD][bB] filter=lfs diff=lfs merge=lfs -text -*.[sS][oO] filter=lfs diff=lfs merge=lfs -text -*.bin filter=lfs diff=lfs merge=lfs -text -*.lib filter=lfs diff=lfs merge=lfs -text -*.a filter=lfs diff=lfs merge=lfs -text -*.param filter=lfs diff=lfs merge=lfs -text -# Packaging -*.7[zZ] filter=lfs diff=lfs merge=lfs -text -*.[bB][zZ]2 filter=lfs diff=lfs merge=lfs -text -*.[gG][zZ] filter=lfs diff=lfs merge=lfs -text -*.[rR][aA][rR] filter=lfs diff=lfs merge=lfs -text -*.[tT][aA][rR] filter=lfs diff=lfs merge=lfs -text -*.[tT][aA][rR].[gG][zZ] filter=lfs diff=lfs merge=lfs -text -*.[zZ][iI][pP] filter=lfs diff=lfs merge=lfs -text -# Fonts -*.[oO][tT][fF] filter=lfs diff=lfs merge=lfs -text -*.[tT][tT][fF] filter=lfs diff=lfs merge=lfs -text -# Documents -*.[pP][dD][fF] filter=lfs diff=lfs merge=lfs -text -# Collapse Unity-generated files on GitHub -*.asset linguist-generated -*.mat linguist-generated -*.meta linguist-generated -*.prefab linguist-generated -*.unity linguist-generated diff --git a/.github/workflows/deploy-mkdocs.yml b/.github/workflows/deploy-mkdocs.yml deleted file mode 100644 index 5566a1d9..00000000 --- a/.github/workflows/deploy-mkdocs.yml +++ /dev/null @@ -1,79 +0,0 @@ -name: Deploy MkDocs to GitHub Pages - -on: - push: - branches: - - master - paths-ignore: - - 'README.md' - - 'docs/all-articles.md' - - 'docs/all-categories.md' - workflow_dispatch: - -permissions: - contents: read - pages: write - id-token: write - -concurrency: - group: pages - cancel-in-progress: true - -jobs: - build: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Configure git for unicode paths - run: git config core.quotepath false - - - name: Setup Python - uses: actions/setup-python@v5 - with: - python-version: "3.x" - - - name: Configure Pages - uses: actions/configure-pages@v5 - - - name: Install dependencies - run: | - pip install mkdocs-material mkdocs-git-revision-date-localized-plugin mkdocs-git-authors-plugin gitpython jieba pyyaml mkdocs-glightbox mkdocs-minify-plugin - - - name: Generate README, all-articles, all-categories and mkdocs nav for build - run: python scripts/generate_readme.py --target all - - - name: Check markdown docs and fail with precise file/line info - run: python scripts/check_docs.py --strict --report-json artifacts/check-report.json --summary-md artifacts/check-summary.md - - - name: Prepare docs directory for MkDocs build - run: python scripts/prepare_mkdocs_docs.py --source docs --output build_docs --mkdocs mkdocs.yml - - - name: Build site - run: mkdocs build --strict --verbose - - - name: Append check summary - if: always() - run: | - if [ -f artifacts/check-summary.md ]; then - cat artifacts/check-summary.md >> "$GITHUB_STEP_SUMMARY" - fi - - - name: Upload Pages artifact - uses: actions/upload-pages-artifact@v3 - with: - path: ./site - - deploy: - environment: - name: github-pages - url: ${{ steps.deployment.outputs.page_url }} - runs-on: ubuntu-latest - needs: build - steps: - - name: Deploy to GitHub Pages - id: deployment - uses: actions/deploy-pages@v4 diff --git a/.github/workflows/docs-autofix.yml b/.github/workflows/docs-autofix.yml deleted file mode 100644 index e14b47ef..00000000 --- a/.github/workflows/docs-autofix.yml +++ /dev/null @@ -1,67 +0,0 @@ -name: Docs Auto Fix - -on: - push: - branches: - - master - paths: - - 'docs/**' - workflow_dispatch: - -permissions: - contents: write - -jobs: - normalize-images: - if: github.actor != 'github-actions[bot]' || !contains(github.event.head_commit.message, '[skip ci]') - runs-on: ubuntu-latest - - steps: - - name: Checkout - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Setup Python - uses: actions/setup-python@v5 - with: - python-version: '3.11' - - - name: Install dependencies - run: pip install pyyaml - - - name: Normalize images - run: python scripts/normalize_images.py --fix --report-json artifacts/image-fix-report.json --summary-md artifacts/image-fix-summary.md - - - name: Append summary - if: always() - run: | - if [ -f artifacts/image-fix-summary.md ]; then - cat artifacts/image-fix-summary.md >> "$GITHUB_STEP_SUMMARY" - fi - - - name: Upload reports - if: always() - uses: actions/upload-artifact@v4 - with: - name: docs-autofix-reports - path: artifacts/ - - - name: Check if docs changed - id: check_changes - run: | - if git diff --quiet -- docs; then - echo "changed=false" >> $GITHUB_OUTPUT - else - echo "changed=true" >> $GITHUB_OUTPUT - fi - - - name: Commit and push changes - if: steps.check_changes.outputs.changed == 'true' - run: | - git config --local user.email "41898282+github-actions[bot]@users.noreply.github.com" - git config --local user.name "github-actions[bot]" - git add docs - git commit -m "docs: normalize image paths and update references [skip ci]" - git pull --rebase origin master - git push origin master diff --git a/.github/workflows/docs-ci.yml b/.github/workflows/docs-ci.yml deleted file mode 100644 index 8705b8d9..00000000 --- a/.github/workflows/docs-ci.yml +++ /dev/null @@ -1,50 +0,0 @@ -name: Docs CI - -on: - pull_request: - branches: - - master - workflow_dispatch: - -permissions: - contents: read - -jobs: - docs-check: - runs-on: ubuntu-latest - - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Setup Python - uses: actions/setup-python@v5 - with: - python-version: '3.11' - - - name: Install dependencies - run: pip install pyyaml - - - name: Check markdown docs - run: python scripts/check_docs.py --strict --report-json artifacts/check-report.json --summary-md artifacts/check-summary.md - - - name: Check image references - run: python scripts/normalize_images.py --check --report-json artifacts/image-check-report.json --summary-md artifacts/image-check-summary.md - - - name: Append summaries - if: always() - run: | - if [ -f artifacts/check-summary.md ]; then - cat artifacts/check-summary.md >> "$GITHUB_STEP_SUMMARY" - fi - if [ -f artifacts/image-check-summary.md ]; then - printf '\n\n' >> "$GITHUB_STEP_SUMMARY" - cat artifacts/image-check-summary.md >> "$GITHUB_STEP_SUMMARY" - fi - - - name: Upload reports - if: always() - uses: actions/upload-artifact@v4 - with: - name: docs-ci-reports - path: artifacts/ diff --git a/.github/workflows/update-readme.yml b/.github/workflows/update-readme.yml deleted file mode 100644 index b3123eff..00000000 --- a/.github/workflows/update-readme.yml +++ /dev/null @@ -1,62 +0,0 @@ -name: Update README and All Articles - -on: - push: - branches: - - master - paths-ignore: - - 'README.md' - - 'docs/all-articles.md' - - 'docs/all-categories.md' - - 'mkdocs.yml' - workflow_dispatch: - -jobs: - update-docs: - if: github.actor != 'github-actions[bot]' || !contains(github.event.head_commit.message, '[skip ci]') - runs-on: ubuntu-latest - - steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Set up Python - uses: actions/setup-python@v5 - with: - python-version: '3.11' - - - name: Install dependencies - run: pip install pyyaml - - - name: Check markdown docs - run: python scripts/check_docs.py --report-json artifacts/check-report.json --summary-md artifacts/check-summary.md || true - - - name: Generate README, all-articles and mkdocs nav - run: python scripts/generate_readme.py --target all - - - name: Append check summary - if: always() - run: | - if [ -f artifacts/check-summary.md ]; then - cat artifacts/check-summary.md >> "$GITHUB_STEP_SUMMARY" - fi - - - name: Check if files changed - id: check_changes - run: | - if git diff --quiet README.md docs/all-articles.md docs/all-categories.md mkdocs.yml; then - echo "changed=false" >> $GITHUB_OUTPUT - else - echo "changed=true" >> $GITHUB_OUTPUT - fi - - - name: Commit and push if changed - if: steps.check_changes.outputs.changed == 'true' - run: | - git config --local user.email "action@github.com" - git config --local user.name "GitHub Action" - git add README.md docs/all-articles.md docs/all-categories.md mkdocs.yml - git commit -m "docs: auto-update README, all-articles, all-categories and mkdocs nav [skip ci]" - git push diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 770b16f2..00000000 --- a/.gitignore +++ /dev/null @@ -1,50 +0,0 @@ -[Ll]ibrary/ -[Tt]emp/ -[Oo]bj/ -[Bb]uild/ -[Bb]uilds/ -Assets/AssetStoreTools* - -# Visual Studio cache directory -.vs/ -.vscode/ - -# Autogenerated VS/MD/Consulo solution and project files -ExportedObj/ -.consulo/ -*.csproj -*.unityproj -*.sln -*.suo -*.tmp -*.user -*.userprefs -*.pidb -*.booproj -*.svd -*.pdb -*.opendb -*.VC.db - -# Unity3D generated meta files -*.pidb.meta -*.pdb.meta -# Unity3D Generated File On Crash Reports -sysinfo.txt - -# Builds -*.apk - -# Crashlytics generated file -Assets/StreamingAssets/crashlytics-build.properties - -resources/_gen/ -.vscode/ -public/ -node_modules/ -/resources -/.history -ignore.log -.hugo_build.lock -.DS_Store -site/ diff --git a/.nojekyll b/.nojekyll new file mode 100644 index 00000000..e69de29b diff --git a/404.html b/404.html new file mode 100644 index 00000000..59246561 --- /dev/null +++ b/404.html @@ -0,0 +1,8281 @@ + + + + + + + + + + + + + + + + + + + + + + + + 王国庆的博客 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+ +
+ +

404 - Not found

+ +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/ANote/00-Test/index.html b/ANote/00-Test/index.html new file mode 100644 index 00000000..4a16bec4 --- /dev/null +++ b/ANote/00-Test/index.html @@ -0,0 +1,8405 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 00-Test - 王国庆的博客 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + 跳转至 + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+ +
+ + + + + + + + + +

00-Test

+

hello world

+

hello

+

+

+

CodeCogsEqn

+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/ANote/2018-09-26/index.html b/ANote/2018-09-26/index.html new file mode 100644 index 00000000..b42ae9aa --- /dev/null +++ b/ANote/2018-09-26/index.html @@ -0,0 +1,8706 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 2018-09-26 - 王国庆的博客 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + 跳转至 + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+ +
+ + + + + + + + + +

2018-09-26

+



+

Pass的通用指令开关

+

LOD:

+

设置:单个设置Shader.maximumLOD、全局设置Shader.globalMaximumLOD、QualitySettings里面的Maximum LODLevel +原理:小于指定值的shader和subshader才能被使用。 +应用:有时候一些显卡虽然支持很多特性,但是效率很低,此时就可以用LOD来进行控制。 +内置shader的LOD值: +  VertexLit kind of shaders = 100 +  Decal, Reflective VertexLit = 150 +  Diffuse = 200 +  Diffuse Detail, Reflective Bumped Unlit, Reflective Bumped VertexLit = 250 +  Bumped, Specular = 300 +  Bumped Specular = 400 +  Parallax = 500 +  Parallax Specular = 600

+

注释:Shader自身的LOD会覆盖全局的LOD。

+

复制代码

+
    public Shader targetShader; private void Start () 
+    { // 全局的值会被Shader本地址覆盖
+        Shader.globalMaximumLOD = 300; if (targetShader != null)
+        {
+            targetShader.maximumLOD = 600;
+        }
+    }
+ +

复制代码

+

RenderQueue:

+
    mtrl.renderQueue = 1000;
+ +

shader中使用ZTest Always,可以让被遮住的物体也渲染。

+

AlphaTest:

+

固定管线:使用AlphaTest命令

+

动态管线:使用clip(alpha - cutoff)指令来实现

+

Alpha检测在ps完成计后,即将写入帧之前,通过和一个固定的数值比较,来决定当前ps的计算结果到底要不要写入帧中。

+
// inside SubShader
+Tags { "Queue"="AlphaTest" "RenderType"="TransparentCutout" "IgnoreProjector"="True" } // inside CGPROGRAM in the fragment Shader:
+clip(textureColor.a - alphaCutoffValue);
+ +

AlphaTest抗锯齿:

+
// inside SubShader
+Tags { "Queue"="AlphaTest" "RenderType"="TransparentCutout" "IgnoreProjector"="True" } // inside Pass
+AlphaToMask On
+ +

AlphaBlend:

+

shader渲染的最后一步,决定怎样将当前计算结果写入到帧缓存中。

+

命令集:

+

Blend BlendOp AlplaToMask

+
// inside SubShader
+Tags { "Queue"="Transparent" "RenderType"="Transparent" "IgnoreProjector"="True" } // inside Pass
+ZWrite Off
+Blend SrcAlpha OneMinusSrcAlpha
+ +

AlphaBlend会有一系列和绘制顺序相关的问题,涉及到的知识点如下:

+

(1)Unity通过Queue保证所有的不透明物体Geometry都会在半透明物体Transparent之前被渲染,(Queue标签决定了这个对象的渲染队列);

+

(2)Unity保证所有Transparent队列的物体,按distance(物体的远近,不是像素的)从后往前渲染;

+

(3)distance的计算方式:使用网格的几何中心点来进行半透明物体的排序。

+

对于部分遮挡的物体,还是会产生不正确的遮挡效果。因此我们要么分割网格,要么使用Alpha Test或者开启ZWrite来替代。

+

AlphaTest和AlphaBlend的性能比较:

+

官方文档在这里:https://docs.unity3d.com/Manual/SL-ShaderPerformance.html

+

翻译如下:

+

固定管线的AlphaTest和可编程管线的clip()函数,在不同平台有不同性能表现:

+

(1)多数平台上,AlphaTest这种整个剔除透明像素的做法能获得一点点性能优势;

+

(2)在ios和android这样基于PowerVR GUPs的设备上,AlphaTest是非常耗资源的,不要企图使用它来做性能优化,因为它会让游戏更慢(是因为直接丢掉像素,让GPUs的某些优化策略没法执行了)。

+

所以结论是:手机上尽量使用AlphaBlend而不是AlphaTest。

+

ColorMask:

+

ColorMask RGB | A | 0 | any combination of R, G, B, A

+

ColorMask也是耗费比较大的操作,只在确实需要时使用。

+

ZWrite/ZTest:

+

ZWrite On | Off

+

ZTest Less | Greater | LEqual | GEqual | Equal | NotEqual | Always

+

关于相机的深度贴图_CameraDepthTexture:

+

(1)默认材质都自带RenderType的Tag;

+

(2)自定义sahder只有添加RenderType标签才会将深度写到_CameraDepthTexture。

+

Offset:

+

对Z深度的偏移。

+

可以让像素看起来更靠前或更靠后,当两个面重叠时,可以手动指定谁相对靠前一些,而且不会战胜z-fitting。

+

Offset只会对ZTest的条件做修正,但是并不会改变最后的Z缓冲。

+

GrabPass:

+

抓取当前屏幕当做贴图使用。

+

复制代码

+
Shader "James/VP Shader/GrabPass" {
+    Properties 
+    {
+        _MainTex("MainTex", 2D) = "white" {}
+    }
+    SubShader 
+    { // 在所有不透明几何体之后自画,这一点很重要
+        Tags { "Queue" = "Transparent" }
+        GrabPass { "_MyGrab" }
+        Pass
+        {
+            CGPROGRAM #pragma vertex vs
+            #pragma fragment ps #include "UnityCG.cginc"
+
+            struct v2f
+            {
+                float4 pos : SV_POSITION;
+                float3 color : COLOR0;
+                float2 uv : TEXCOORD0;
+            };
+
+            sampler2D _MainTex;
+            float4 _MainTex_ST;
+            sampler2D _MyGrab;
+
+            v2f vs(appdata_base v)
+            {
+                v2f o;
+                o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
+                o.uv = TRANSFORM_TEX(v.texcoord, _MainTex); return o;
+            }
+
+            float4 ps(v2f i):COLOR
+            {
+                float4 texColor = tex2D(_MainTex, i.uv);
+                float4 grabColor = tex2D(_MyGrab, i.uv); return texColor * grabColor;
+            }
+            ENDCG
+        }
+    } 
+    FallBack "Diffuse" }
+ +

复制代码

+

Fog:

+

雾效实现的三种方式:

+

(1)全局雾

+
  RenderSettings.fog = true;
+  RenderSettings.fogColor = Color.red;
+  RenderSettings.fogMode = FogMode.Linear;
+  RenderSettings.fogStartDistance = 0;
+  RenderSettings.fogEndDistance = 10;
+ +

(2)Fog指令

+
  Fog{ Mode Linear Color(1, 0, 0) Range 0, 10 }
+ +

(3)Shader计算方式

+

复制代码

+
Shader "James/VP Shader/Fog" {
+    Properties {
+        _MainTex ("Base (RGB)", 2D) = "white" {}
+        _FogColor("FogColor", Color) = (1, 1, 1, 1)
+        _Density("Density", Range(0, 10)) = 1 _NearDistance("NearDistance", Float) = 0 _FarDistance("FarDistance", Float) = 10 }
+    SubShader {
+        Tags { "RenderType"="Opaque" }
+        Fog { Mode Off }
+        Pass
+        {
+            CGPROGRAM #pragma vertex vs
+            #pragma fragment ps #include "UnityCG.cginc"
+
+            struct v2f
+            {
+                float4 pos : SV_POSITION;
+                float3 color : COLOR0;
+                float2 uv : TEXCOORD0;
+                float4 depth : TEXCOORD1;
+            };
+
+            sampler2D _MainTex;
+            float4 _MainTex_ST;
+            float4 _FogColor; float _Density; float _NearDistance; float _FarDistance;
+
+            v2f vs(appdata_base v)
+            {
+                v2f o;
+                o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
+                o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
+                o.depth = mul(UNITY_MATRIX_MV, v.vertex);
+                o.depth.z = -o.depth.z;
+                o.depth.w = (_FarDistance - _NearDistance) * o.depth.w; return o;
+            }
+
+            float4 ps(v2f i):COLOR
+            {
+                float4 texColor = tex2D(_MainTex, i.uv); float fg = 0; if(i.depth.z > _NearDistance && i.depth.z < _FarDistance)
+                {
+                    fg = i.depth.z / i.depth.w;
+                } else if(i.depth.z > _FarDistance)
+                {
+                    fg = _FarDistance / i.depth.w;
+                } return fg * _Density * _FogColor * texColor;
+            }
+            ENDCG
+        }
+    } 
+    FallBack "Diffuse" }
+ +

复制代码

+

Stencil:

+

Stencil-Test在Z-Test和Alpha-Test之前,如果模板检测不通过,则像素直接被丢掉而不会执行fragment函数。

+

复制代码

+
Shader "James/VP Shader/Stencil" {
+    Properties {
+        _MainTex ("Base (RGB)", 2D) = "white" {}
+        _refVal("Stencil Ref Value",int)=0 }
+    SubShader {
+        Tags { "RenderType"="Opaque" }
+        ZTest Always
+        Stencil
+        {
+            Ref [_refVal]
+            Comp GEqual
+            Pass Replace
+            Fail keep
+            ZFail keep
+        }
+        Pass
+        {
+            CGPROGRAM #pragma vertex vs
+            #pragma fragment ps #include "UnityCG.cginc"
+
+            struct v2f
+            {
+                float4 pos : SV_POSITION;
+                float3 color : COLOR0;
+                float2 uv : TEXCOORD0;
+            };
+
+            sampler2D _MainTex;
+            float4 _MainTex_ST;
+
+            v2f vs(appdata_base v)
+            {
+                v2f o;
+                o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
+                o.uv = TRANSFORM_TEX(v.texcoord, _MainTex); return o;
+            }
+
+            float4 ps(v2f i):COLOR
+            {
+                float4 texColor = tex2D(_MainTex, i.uv); return texColor;
+            }
+            ENDCG
+        }
+    } 
+    FallBack "Diffuse" }
+ +

[

+

复制代码](javascript:void(0); "复制代码")

+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git "a/ANote/CSharp\347\254\224\350\256\260/index.html" "b/ANote/CSharp\347\254\224\350\256\260/index.html" new file mode 100644 index 00000000..8daab4c3 --- /dev/null +++ "b/ANote/CSharp\347\254\224\350\256\260/index.html" @@ -0,0 +1,9190 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + CSharp笔记 - 王国庆的博客 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + 跳转至 + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ +
+ + + + + + + + + +

CSharp笔记

+
+
    +
  • [ ] 绍跳点搜索(JPS)算法
  • +
+

IL学习:
+C#基础拾遗系列之一:先看懂IL代码
+读懂IL代码就这么简单 (一) +c# IL 指令集
+c#的逆向工程-IL指令集
+IL指令详细表

+
+

vs中设置IL ( ildasm.exe )

+

我这里的路径: +C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.8 Tools\x64\ildasm.exe +

+

+

int强转 enum过程

+

结论:int强转enum和enum正常赋值是一样的,强转失败不会报异常,且赋值成功

+
using System;
+namespace EnumTest
+{
+    internal class Program
+    {
+        enum Hello
+        {
+            A=0xfff0,
+            B= 0xfff1,
+        }
+
+        static void Main(string[] args)
+        {
+            int n = 0xfff0;
+            int m = 0xfff2;
+            // int强转enum和enum正常赋值是一样的开销 不做越界处理
+            Hello h1=(Hello)n;
+            int b1 = m;
+
+
+            Console.WriteLine(h1);
+            Console.WriteLine(b1);
+            Console.ReadKey();
+        }
+    }
+}
+
+
+

生成exe(Debug模式下,Release模式下代码被优化)后查看id代码

+
//Debug模式
+.method private hidebysig static void  Main(string[] args) cil managed
+{
+  .entrypoint
+  // 代码大小       43 (0x2b)
+  .maxstack  1
+  .locals init ([0] int32 n,
+           [1] int32 m,
+           [2] valuetype EnumTest.Program/Hello h1,
+           [3] int32 b1) //局部变量索引
+  IL_0000:  nop
+  IL_0001:  ldc.i4     0xfff0  //入栈
+  IL_0006:  stloc.0 // n赋值 栈顶出栈
+  IL_0007:  ldc.i4     0xfff2  //入栈
+  IL_000c:  stloc.1 // m赋值  栈顶出栈
+  IL_000d:  ldloc.0 //加载 入栈 n
+  IL_000e:  stloc.2 // h1=n  栈顶出栈
+  IL_000f:  ldloc.1 // 加载 入栈 m
+  IL_0010:  stloc.3 // b1=m 栈顶出栈
+  IL_0011:  ldloc.2 //加载 入栈 h1
+  IL_0012:  box        EnumTest.Program/Hello //栈顶h1出栈 装箱结果入栈
+  IL_0017:  call       void [mscorlib]System.Console::WriteLine(object)  //使用一个参数,使用后栈顶出栈
+  IL_001c:  nop
+  IL_001d:  ldloc.3 //加载 入栈 b1 
+  IL_001e:  call       void [mscorlib]System.Console::WriteLine(int32)
+  IL_0023:  nop
+  IL_0024:  call       valuetype [mscorlib]System.ConsoleKeyInfo [mscorlib]System.Console::ReadKey()
+  IL_0029:  pop
+  IL_002a:  ret
+} // end of method Program::Main
+
+
+
//Relase模式
+.method private hidebysig static void  Main(string[] args) cil managed
+{
+  .entrypoint
+  // 代码大小       36 (0x24)
+  .maxstack  2
+  .locals init ([0] int32 m,
+           [1] int32 b1)
+  IL_0000:  ldc.i4     0xfff0
+  IL_0005:  ldc.i4     0xfff2
+  IL_000a:  stloc.0 // m=0xfff2 
+  IL_000b:  ldloc.0 
+  IL_000c:  stloc.1 // b1=0xfff0
+  IL_000d:  box        EnumTest.Program/Hello
+  IL_0012:  call       void [mscorlib]System.Console::WriteLine(object)
+  IL_0017:  ldloc.1 //加载b1
+  IL_0018:  call       void [mscorlib]System.Console::WriteLine(int32)
+  IL_001d:  call       valuetype [mscorlib]System.ConsoleKeyInfo [mscorlib]System.Console::ReadKey()
+  IL_0022:  pop
+  IL_0023:  ret
+} // end of method Program::Main
+
+
+

int强转成enum且越界

+

结论:int强转成enum越界时赋值会成功,但是输出是int的值 +代码

+
using System;
+namespace EnumTest
+{
+    internal class Program
+    {
+        enum Hello
+        {
+            A=0xfff0,
+            B= 0xfff1,
+        }
+
+        static void Main(string[] args)
+        {
+            int n = 0xfff0;
+            int m = 0xfff2;
+
+            Hello h1=(Hello)n;
+            int b1 = m;
+
+            Console.WriteLine(h1.ToString());
+            h1 = (Hello)100;
+            //int强转成enum越界时赋值会成功,但是输出是int的值
+            Console.WriteLine(h1.ToString());
+            Console.WriteLine(b1);
+            Console.ReadKey();
+        }
+    }
+}
+
+
+

输出:

+
A
+100
+65522
+
+

在类和结构之间进行选择(Choosing Between Class and Struct)

+
+

https://docs.microsoft.com/en-us/dotnet/standard/design-guidelines/choosing-between-class-and-struct

+
+

结论:

+

✔️ 如果类型的实例很小且通常短暂存在或通常嵌入在其他对象中,请考虑定义结构而不是类。

+

❌ 避免定义结构,除非该类型具有以下所有特征: +- 它在逻辑上表示单个值,类似于原始类型(int、double等)。 +- 它的实例大小小于 16 个字节。 +- 它是不可变的。 +- 它不必经常装箱。

+
+

可以这么理解:值类型的数组是真正的线性结构;而引用类型数组类似树结构。因此相率上存在天然差距!

+

引用类型的对象数组,物理上一般是分配指针来指向引用的实例,此时数组的内存块不能涵盖所有要访问的数据。而struct数组在这种情况下所有会用到的数据都在数组的物理内存之中包含,可以直接访问到,无需通过GC堆内存的对象引用来反复的间接查找。同时,如果实例数量非常多时,使用struct数组还能避免大量分散在GC堆中的对象实例,从而减轻GC压力。这里理想化的认为struct的定义中所有字段都是值类型的,不包含string等引用类型。

+
+

对比:

+
+--------------------------------------------------+------+----------------------------------------------+
+|                      Struct                      |      |                      Class                    |
++--------------------------------------------------+------+----------------------------------------------+
+| - 1 per Thread.                                  |      | - 1 per application.                         |
+|                                                  |      |                                              |
+| - Holds value types.                             |      | - Holds reference types.                     |
+|                                                  |      |                                              |
+| - Types in the stack are positioned              |      | - No type ordering (data is fragmented).     |
+|   using the LIFO principle.                      |      |                                              |
+|                                                  |      |                                              |
+| - Can't have a default constructor and/or        |      | - Can have a default constructor             |
+|   finalizer(destructor).                         |      |   and/or finalizer.                          |
+|                                                  |      |                                              |
+| - Can be created with or without a new operator. |      | - Can be created only with a new operator.   |
+|                                                  |      |                                              |
+| - Can't derive from the class or struct          |  VS  | - Can have only one base class and/or        |
+|   but can derive from the multiple interfaces.   |      |   derive from multiple interfaces.           |
+|                                                  |      |                                              |
+| - The data members can't be protected.           |      | - Data members can be protected.             |
+|                                                  |      |                                              |
+| - Function members can't be                      |      | - Function members can be                    |
+|   virtual or abstract.                           |      |   virtual or abstract.                       |
+|                                                  |      |                                              |
+| - Can't have a null value.                       |      | - Can have a null value.                     |
+|                                                  |      |                                              |
+| - During an assignment, the contents are         |      | - Assignment is happening                    |
+|   copied from one variable to another.           |      |   by reference.                              |
++--------------------------------------------------+------+----------------------------------------------+
+
+

WinDbg

+
+

Windbg调试命令详解 +https://docs.microsoft.com/zh-cn/windows-hardware/drivers/debugger/debugger-download-tools +https://docs.microsoft.com/zh-cn/sysinternals/downloads/procdump +https://www.cnblogs.com/harrychinese/p/winbug.html +https://cloud.tencent.com/developer/article/1045085 +http://fresky.github.io/2015/06/21/how-to-attack-the-memory-leak-issue/ +生产环境诊断利器 WinDbg 帮你快速分析异常情况 Dump 文件 +windbg调试命令

+
+

WinDbg安装

+

安装方式(建议使用该方法)1

+

使用windows store 搜索 WinDbg preview 然后下载 +

+

安装方式2:

+

下载windows SDK,并且勾选WinDbg;

+

如果已经安装windows SDK 使用修复 : + + + +

+

配置符号表环境变量

+

添加环境变量

+
_NT_SYMBOL_PATH=C:\Symbols;srv*C:\Symbols*http://msdl.microsoft.com/download/symbols;
+
+_NT_ALT_SYMBOL_PATH=cache*C:\Symbols
+
+

+

WinDBG中加载SOS和CLR (安装sos工具)

+
#step1
+dotnet tool install -g dotnet-sos
+#step2 
+dotnet-sos install
+
+#安装成功后有SOS install succeeded的提示,并且返回有安装路径。
+PS E:\Project> dotnet-sos install
+Installing SOS to C:\Users\coding\.dotnet\sos
+Installing over existing installation...
+Creating installation directory...
+Copying files from C:\Users\coding\.dotnet\tools\.store\dotnet-sos\6.0.328102\dotnet-sos\6.0.328102\tools\netcoreapp3.1\any\win-x64
+Copying files from C:\Users\coding\.dotnet\tools\.store\dotnet-sos\6.0.328102\dotnet-sos\6.0.328102\tools\netcoreapp3.1\any\lib
+Execute '.load C:\Users\coding\.dotnet\sos\sos.dll' to load SOS in your Windows debugger.
+Cleaning up...
+SOS install succeeded
+
+

命令 !DumpHeap -stat不可用

+
.loadby sos clr 
+如果失败需要 (sos.dll和clr.dll一般情况下在同一个目录)
+.load 绝对路径的sos.dll
+.load 绝对路径的clr.dll
+
+
+

https://www.cnblogs.com/Leo_wl/p/3329437.html +https://blog.csdn.net/ghhong1988/article/details/104674837 +想要一次性成功搭建测试环境,那得靠人品。看来我近来人品积累的不够,不断的有小问题出现。比如加载SOS和CLR,就让我不胜其烦。必须得记下来,分享出来,以节省大家的时间。

+

问题一:WinDBG分X86和X64两个版本

+

如果你用的是32位的WinDBG,那直接打开就行;你如果用的是64位的版本,那么如果调试64位代码也直接打开,如果调试x86的代码,要使用Wow64下的WinDBG.exe。

+

问题二:确定SOS和CLR的位置和版本

+

如果安装了Visual Studio的机器,可以打开VS的命令行,输入where sos.dll命令,可以找到sos.dll的全路径(需要说明的是,找到的不一定是全部的文件)。它的一般位置在C:\Windows\Microsoft.NET\Framework?\version?\SOS.dll。其中Framework?包括Framework和Framework64两个版本;version?包括v2.0.50727,v3.0,v3.5和v4.0.30319等版本。文件确切路径的选择依据要调试程序的版本而定,一般为C:\Windows\Microsoft.NET\Framework\v4.0.30319\SOS.dll,CLR为同一目录下的CLR.dll文件。

+

问题三:加载SOS和CLR

+

运气好的话,使用命令.load C:\Windows\Microsoft.NET\Framework\v4.0.30319\SOS.dll可以加载成功。如果失败,特别是出现The call to LoadLibrary(C:\Windows\Microsoft.NET\Framework\v4.0.30319\sos.dll) failed, Win32 error 0n193这样的错误,请确认加载sos.dll的版本是否正确。

+

此外,加载不出错,并不见得可以直接使用。可以尝试命令.loadby sos clr。如果命令成功,那么测试环境好了。如果出现了“Unable to find module 'clr'”这样的错误。请键入g让调试程序运行一会儿,停下来的时候再尝试命令.loadby sos clr,这时一般都会成功。 +

+

Windbg+sos调试.net笔记

+

要加载扩展,有2个命令。一个是.loadby,另一个是.load。对于.loadby,请使用相对路径;对于.load,请使用完整路径。

+

对于.loadby,有5个选项: +- loadby sos mscorsvr +- loadby sos mscorwks +- loadby sos clr +- loadby sos coreclr +- loadby sos

+

其中mscorsvr确实很旧(.NET CLR 1,服务器版本),mscorwks确实很旧(.NET CLR 1和2,但仍然存在),clr是在当今很常见(.NET CLR 4),coreclr可能正在增加(UWP和Silverlight)

+
+

当尚未加载.NET运行时时,您正在尝试加载SOS。等待直到加载.NET,然后该命令将起作用。在初始断点处肯定是不可能的。 sxe ld clr 表示让应用程序运行到.NET可用

+
    sxe ld clr
+    sxe ld mscorwks
+    sxe ld coreclr
+    g
+
+

生成转储文件

+

方式1

+

任务管理器,进程 -> 创建转储文件 ;得到当前进程的dump文件

+

方式2

+

使用ProcDump 工具

+
# mydotNetApp.exe是进程名字
+procdump -ma mydotNetApp.exe d:\myapp.dmp
+# TestCase.exe是进程名字
+.\procdump.exe -ma TestCase.exe e:\test.dmp
+
+

方式3 +使用vs调试,断点后,点击 调试菜单-> 将转储另存为 +

+

配置windbg为vs外部工具

+
Windbg(x86)
+
+C:\Users\coding\AppData\Local\Microsoft\WindowsApps\WinDbgX.exe
+C:\Program Files (x86)\Windows Kits\10\Debuggers\x86\windbg.exe
+
+ -srcpath $(ProjectDir) $(TargetPath)
+
+ $(TargetDir)
+
+

+
C:\Symbols
+srv*C:\Symbols*http://msdl.microsoft.com/download/symbols
+
+cache*C:\Symbols
+
+ !DumpHeap -stat
+
+0:000> !clrstack
+No export clrstack found
+0:000> !dumpstackobjects
+No export dumpstackobjects found
+0:000> sxe ld clr
+0:000> sxe ld mscorwks
+0:000> sxe ld coreclr
+
+

windbg 基础

+

windbg命令分标准命令、元命令和扩展命令。

+

标准命令提供基本功能,不区分大小写。比如dt、lm、g、bl、bc、p等。

+

提供标准命令没有提供的功能,也内建在调试引擎中,以.开头。如.sympath .reload等。

+

扩展命令用于扩展某一方面的调试功能,实现在动态加载的扩展模块中,以!开头。如!analyze等。

+

使用;作为分隔符,可以在同一行输入多条命令。

+

一次可以执行多条命令,命令间用分号;分隔 【如:bp main;bp view.cpp:120】,一次打2个断点。

+

windbg调试C#程序

+
+

使用WinDBG + SOS调试.Net程序的一般步骤

+

windbg调试C#代码

+

用windbg调试C#代码是比较麻烦的,因为windbg是针对OS层级的,而C#被CLR隔了一层,很多原生的命令如查看局部变量dv、查看变量类型dt等在CLR的环境中都不能用了。必须使用针对CLR的扩展命令,比如sos、psscor2中的命令。

+

sos官网文档 +使用 Windows 调试器调试托管代码

+

+
+
    sxe ld clr # 添加中断 (加载指定名称的dll时,调试器中断) 
+    # sxe ld mscorwks  # 添加中断 (加载指定名称的dll时,调试器中断) 
+    # sxe ld coreclr  # 添加中断 (加载指定名称的dll时,调试器中断) 
+    # sxe ld clrjit  # 添加中断 (加载指定名称的dll时,调试器中断) 
+    g # 运行,当遇到中断或者断点时 中断
+    # 让应用程序运行到.NET可用(clr等核心初始化后,这时候.net环境初始化完成)
+    .loadby sos clr  ## 加载sos模块 (宿主程序必须加载clr后执行,否则会找不到clr)
+
+    !bpmd DotnetTest DotnetTest.Program.Main  ## Main函數添加断点
+    !bpmd -md 005e4d90 #在指定的MethodDesc地址处添加断点
+    g # 运行,当遇到中断或者断点时 中断
+    !name2ee DotnetTest DotnetTest.Program.Main
+
+
.cordll -ve -u -l
+lmv mclr #(适用于 CLR 4.0 版)
+
+lmv mscorwks  #(适用于 CLR 的 1.0 或 2.0 版)
+
+!sos.help
+!sos.Threads
+!sos.DumpDomain
+
+k
+kv
+kb
+x *! 
+
+
+

.load.loadby 命令将新的扩展DLL加载到调试器中。

+

使用形式

+
.load DLLName
+!DLLName.load
+.loadby DLLName ModuleName
+
+参数
+DLLName
+指定要加载的调试器扩展DLL。如果使用.load命令,DLLName应包含完整路径。如果使用.loadby命令,DLLName应仅包含文件名。
+
+ModuleName
+指定与DLLName指定的扩展DLL位于同一目录中的模块的模块名。
+
+
+

sxe ld

+

有些场景需要使用windbg调试某个dll模块,而这个模块加载时机不是很确定。

+

通常需要使用sxe ld 来设置一个模块加载异常。当被调试进程加载指定名称的dll时,调试器就会中断,后续就可以对该模块的设置一些符号断点了。

+

lm.chain

+

查看已经加载模块的列表

+

Name2EE <module name> <type or method name>

+
+

Name2EE <module name>!<type or method name>

+
+

显示指定模块中指定类型或方法的MethodTable结构和结构。指定的模块必须在进程中加载​​。要获得正确的类型名称,请使用Ildasm.exe (IL Disassembler)浏览模块。您还可以作为模块名称参数传递以搜索所有加载的托管模块。模块名称参数也可以是模块的调试器名称,例如或。此命令支持 < > < > 的 Windows 调试器语法。类型必须是完全限定的。EEClass

+
0:000> !name2ee DotnetTest DotnetTest.Program.Main
+Module:      00c44044
+Assembly:    DotnetTest.exe
+Token:       06000009
+MethodDesc:  00c44d90
+Name:        DotnetTest.Program.Main(System.String[])
+Not JITTED yet. Use !bpmd -md 00c44d90 to break on run.
+
+

BPMD [-nofuturemodule] [<module name> <method name>] [-md <MethodDesc>] -list -clear <pending breakpoint number> -clearall

+

在指定模块中的指定方法处创建断点。

+

如果指定的模块和方法尚未加载,则此命令在创建断点之前等待模块已加载和即时 (JIT) 编译的通知。

+

您可以使用-list、-clear和-clearall选项管理挂起断点的列表:

+

-list选项生成所有挂起断点的列表。如果挂起的断点具有非零模块 ID,则该断点特定于该特定加载模块中的函数。如果挂起的断点的模块 ID 为零,则该断点适用于尚未加载的模块。

+

使用-clear或-clearall选项从列表中删除挂起的断点。

+
# 添加断点
+!bpmd DotnetTest DotnetTest.Program.Main  #[<module name> <method name>]
+!bpmd DotnetTest DotnetTest.HeapStack.New
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git "a/ANote/CSharp\350\256\260\345\275\225/index.html" "b/ANote/CSharp\350\256\260\345\275\225/index.html" new file mode 100644 index 00000000..692008f0 --- /dev/null +++ "b/ANote/CSharp\350\256\260\345\275\225/index.html" @@ -0,0 +1,8408 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + CSharp记录 - 王国庆的博客 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + 跳转至 + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+ +
+ + + + + + + + + +

CSharp记录

+

垃圾回收的基本知识:
+https://github.com/dotnet/docs.zh-cn/blob/live/docs/standard/garbage-collection/fundamentals.md
+垃圾回收的基本知识:
+https://docs.microsoft.com/zh-cn/dotnet/standard/garbage-collection/fundamentals

+

清理未托管资源:
+https://docs.microsoft.com/zh-cn/dotnet/standard/garbage-collection/unmanaged

+

.NET documentation 中文版:
+https://docs.microsoft.com/zh-cn/dotnet/fundamentals/

+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git "a/ANote/DOTS\347\254\224\350\256\260/index.html" "b/ANote/DOTS\347\254\224\350\256\260/index.html" new file mode 100644 index 00000000..73cb392b --- /dev/null +++ "b/ANote/DOTS\347\254\224\350\256\260/index.html" @@ -0,0 +1,8412 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + DOTS笔记 - 王国庆的博客 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + 跳转至 + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+ +
+ + + + + + + + + +

DOTS笔记

+

+

+

+

+

+

+

+

+

+

+

+

+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git "a/ANote/DrawCall\344\274\230\345\214\226\345\256\236\346\210\230\346\274\224\350\256\262\347\250\277/index.html" "b/ANote/DrawCall\344\274\230\345\214\226\345\256\236\346\210\230\346\274\224\350\256\262\347\250\277/index.html" new file mode 100644 index 00000000..8310be14 --- /dev/null +++ "b/ANote/DrawCall\344\274\230\345\214\226\345\256\236\346\210\230\346\274\224\350\256\262\347\250\277/index.html" @@ -0,0 +1,8469 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + DrawCall优化实战演讲稿 - 王国庆的博客 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + 跳转至 + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+ +
+ + + + + + + + + +

DrawCall优化实战演讲稿

+

世界地图合批优化实战分享

+

+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git "a/ANote/EOC\351\241\271\347\233\256\345\210\206\346\236\220\347\254\224\350\256\2601/index.html" "b/ANote/EOC\351\241\271\347\233\256\345\210\206\346\236\220\347\254\224\350\256\2601/index.html" new file mode 100644 index 00000000..0d74fd6f --- /dev/null +++ "b/ANote/EOC\351\241\271\347\233\256\345\210\206\346\236\220\347\254\224\350\256\2601/index.html" @@ -0,0 +1,8816 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + EOC项目分析笔记1 - 王国庆的博客 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + 跳转至 + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ +
+ + + + + + + + + +

EOC项目分析笔记1

+

项目优化列表

+
    +
  • +

    [ ] UI生成的view文件,这里执行比较浪费效率,有可能无用的节点也会Find transform 节点 + + 方案:使用C#属性的方式去处理,只有使用的时候才get,能有效的减少无用节点的使用

    +
  • +
  • +

    [ ] 字符串拼接问题,使用公用 StringBuilder 进行处理

    +
  • +
+
using System.Text;
+using Sproto;
+
+namespace Skyunion
+{
+    public static class StringBuilderCache
+    {
+        private const int MAX_Size = 512;
+        private static StringBuilder s_Cache = new StringBuilder(MAX_Size, MAX_Size);
+
+        public static StringBuilder Get(int capacity = 0)
+        {
+            if (s_Cache.Capacity < capacity)
+                return new StringBuilder(capacity);
+            s_Cache.Length = 0;
+            return s_Cache;
+        }
+    }
+}
+
+
    +
  • +

    [ ] 所有new 数组的地方建议使用公共方法去管理,或者组成一个共有池

    +
  • +
  • +

    [ ] 所有使用 Physics.RaycastAllPhysics.Raycast的地方修改成无GC的方法

    +
  • +
+
public static int RaycastNonAlloc(Ray ray, RaycastHit[] results, float maxDistance, int layerMask)
+
+

+
    +
  • [x] 对比字符串查找业务对象和id查找业务对象耗时及HashCode获取业务对象
    +

    方式1:
    +点击gameObject 根据GameObject的name字符串查找对应的业务对象
    +方式2:
    +初始化gameObject时添加一个组件,组件内有id字段,点击gameObject时根据gameObject上的组件找到id,然后根据id找到对应业务对象
    +方式3:
    +点击gameObject,根据gameObject的HashCode找到对应业务对象

    +
    +
  • +
+

对比耗时:
+使用800000个gameObject测试:
+

+

方式1的耗时:重命名gameObject+通过字符串查找业务对象; 耗时 503+316 毫秒 +方式2的耗时:给gameObject添加组件+通过id查找业务对象; 耗时 3883+191 毫秒 +方式3的耗时:gameObject获取HashCode+通过HashCode查找业务对象;耗时 34+12 毫秒

+

结论:
+gameObject的HashCode查找效率最高,其次是字符串查找,添加组件绑定id的方式最慢

+
    +
  • [ ] UGUI 优先使用RectMask2D,最后使用Mask
  • +
+

登录流程

+

设置登录信息,并且派发登录事件(开发模式) + +设置登录信息,并且派发登录事件(线上模式) + +链接服务器 + +连接服务器后进行重定向,再次连接服务器,并且验证;重定向且验证后表示登录成功

+

登录成功后获取角色列表 +

+

获取角色回调后后使用第一个角色 + +

+

使用第一个角色进行登录,登录角色服
+发送: SprotoType.Role_RoleLogin +

+

收到登录角色服的回调,返回角色信息 +

+

+

+

内城初始化

+

地图初始化

+

+

相机位置初始化

+

相机参数初始化 +

+

+

+

+

相机视野变化和同步视野到服务器

+
+

监听 WorldCamera.Instance().AddViewChange(OnWorldViewChange); +
+
+
+*发送给服务器相机移动的消息 public void SendMapMove(long x, long y) *

+
+
FogSystemMediator.cs(300):WorldCamera.Instance().AddViewChange(OnMapViewChange);
+GlobalViewLevelMediator.cs(553):WorldCamera.Instance().AddViewChange(OnWorldViewChange);
+MapUIiconMyCityMediator.cs(175):WorldCamera.Instance().AddViewChange(OnWorldViewChange);
+PVPGlobalMediator.cs(468):WorldCamera.Instance().AddViewChange(OnWorldViewChange);
+WorldMgrMediator.cs(312):WorldCamera.Instance().AddViewChange(OnWorldViewChange);
+MainInterfaceMediator.cs(724):WorldCamera.Instance().AddViewChange(OnWorldViewChange);
+WorldPanelMediator.cs(96):WorldCamera.Instance().AddViewChange(OnWorldViewChange);
+
+

地图单位初始化

+

初始化添加单位及更新单位数据 + + +相机视野改变,调整单位 +

+

单位资源加载

+
public static void WorldMapViewObjFactory.SysLoadWorldObj(MapObjectInfoEntity data, string prefabname, Action<GameObject, MapObjectInfoEntity> callback)
+
+public void WorldMgrMediator.OnLoadMapObj(GameObject go,MapObjectInfoEntity data)
+
+

地图地表及装饰物初始化

+
public void ReadMapBriefDataFromFile2(string map_data_path, int server_x, int server_y, Action action = null)
+
+

+

+

LOD管理

+

联盟区域数据及显示

+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git "a/ANote/EOC\351\241\271\347\233\256\347\254\224\350\256\260/index.html" "b/ANote/EOC\351\241\271\347\233\256\347\254\224\350\256\260/index.html" new file mode 100644 index 00000000..c62383dd --- /dev/null +++ "b/ANote/EOC\351\241\271\347\233\256\347\254\224\350\256\260/index.html" @@ -0,0 +1,8690 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + EOC项目笔记 - 王国庆的博客 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + 跳转至 + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+ +
+ + + + + + + + + +

EOC项目笔记

+
    +
  • [ ] ScrollRect 工具封装
  • +
+

SProto 分析

+
     int Pack(Sproto.SprotoTypeBase o)
+        {
+            byte[] encodeWarpPackage;
+            int gameSession;
+            long sprotoId;
+            byte[] token = new byte[0];
+            pack.Encode(o, ref token, out encodeWarpPackage, out gameSession, out sprotoId);
+            return encodeWarpPackage.Length;
+        }
+
+        NetMsgPackage pack = new NetMsgPackage();
+        Role_CreateArmy.request req ;
+        public override void Update()
+        {
+            if (Input.GetKeyUp(KeyCode.Y))
+            {
+                req = new Role_CreateArmy.request();
+                Debug.LogError($" 0 >>>{Pack(req)}>>>{Codingriver.Dumper.Dump(req)}");
+                req = new Role_CreateArmy.request();
+                req.targetType = 1;
+                Debug.LogError($" 1 >>>{Pack(req)}>>>{Codingriver.Dumper.Dump(req)}");
+                req = new Role_CreateArmy.request();
+                req.targetType = 1;
+                req.isSituStation = true;
+                Debug.LogError($" 2 >>>{Pack(req)}>>>{Codingriver.Dumper.Dump(req)}");
+                req = new Role_CreateArmy.request();
+                req.targetType = 1;
+                req.isSituStation = true;
+                req.targetArg = new MarchTargetArg { targetName = "hello" };
+                Debug.LogError($" 3 >>>{Pack(req)}>>>{Codingriver.Dumper.Dump(req)}");
+            }
+            if(Input.GetKeyUp(KeyCode.U))
+            {
+                req = new Role_CreateArmy.request();
+                req.heros = new List<MarchHero>(1);
+                req.heros.Add(new MarchHero() { heroId = 2 });
+                Debug.LogError($" map 1 >>1 >>>{Pack(req)}>>>{Codingriver.Dumper.Dump(req)}");
+                req = new Role_CreateArmy.request();
+                req.heros = new List<MarchHero>(1);
+                req.heros.Add(new MarchHero(){ heroId = 2   });
+                req.heros.Add(new MarchHero(){ heroId = 3   });
+                req.heros.Add(new MarchHero(){ heroId = 4   });
+                req.heros.Add(new MarchHero(){ heroId = 200 });
+                req.heros.Add(new MarchHero(){ heroId = 300 });
+                req.heros.Add(new MarchHero(){ heroId = 400 });
+                req.heros.Add(new MarchHero(){ heroId = 20  });
+                req.heros.Add(new MarchHero(){ heroId = 30  });
+                req.heros.Add(new MarchHero(){ heroId = 40  });
+                Debug.LogError($" map 1 >>>{Pack(req)}>>>{Codingriver.Dumper.Dump(req)}");
+                req = new Role_CreateArmy.request();
+                req.heros = new List<MarchHero>(1);
+                req.heros.Add(new MarchHero() { heroId = 2 });
+                req.heros.Add(new MarchHero() { heroId = 3 });
+                req.heros.Add(new MarchHero() { heroId = 4 });
+                req.heros.Add(new MarchHero() { heroId = 200 });
+                req.heros.Add(new MarchHero() { heroId = 300 });
+                req.heros.Add(new MarchHero() { heroId = 400 });
+                req.heros.Add(new MarchHero() { heroId = 20 });
+                req.heros.Add(new MarchHero() { heroId = 30 });
+                req.heros.Add(new MarchHero() { heroId = 40 });
+                req.targetType = 1;
+                req.isSituStation = true;
+                Debug.LogError($" map 4 >>>{Pack(req)}>>>{Codingriver.Dumper.Dump(req)}");
+            }
+            if (Input.GetKeyUp(KeyCode.I))
+            {
+                req = new Role_CreateArmy.request();
+                req.soldiers = new Dictionary<long, SoldierInfo>();
+                req.soldiers[2] = new SoldierInfo { id = 2 };
+                req.soldiers[3] = new SoldierInfo { id = 3 };
+                req.soldiers[4] = new SoldierInfo { id = 4 };
+                req.soldiers[5] = new SoldierInfo { id = 200 };
+                req.soldiers[6] = new SoldierInfo { id = 300 };
+                req.soldiers[7] = new SoldierInfo { id = 400 };
+                req.soldiers[8] = new SoldierInfo { id = 20 };
+                req.soldiers[9] = new SoldierInfo { id = 30 };
+                req.soldiers[10]= new SoldierInfo { id = 40 };
+                req.heros = new List<MarchHero>(1);
+                req.heros.Add(new MarchHero() { heroId = 2 });
+                req.heros.Add(new MarchHero() { heroId = 3 });
+                req.heros.Add(new MarchHero() { heroId = 4 });
+                req.heros.Add(new MarchHero() { heroId = 200 });
+                req.heros.Add(new MarchHero() { heroId = 300 });
+                req.heros.Add(new MarchHero() { heroId = 400 });
+                req.heros.Add(new MarchHero() { heroId = 20 });
+                req.heros.Add(new MarchHero() { heroId = 30 });
+                req.heros.Add(new MarchHero() { heroId = 40 });
+                req.targetType = 1;
+                req.isSituStation = true;
+                Debug.LogError($" map 5 >>>{Pack(req)}>>>{Codingriver.Dumper.Dump(req)}");
+            }
+        }
+
+

+

AssetBundle 包体大小

+
+

当前冗余资源 111 ,总资源数 5430 +

+
+

Shader打包问题

+
+

shader的ab文件 all_shader.ab 大小 1291kb +shader.ab 大小 302kb +(shader.ab内容) +shader.ab

+
+

依赖信息异常

+

(all_shader.ab内容) 应该是把所以shader打包进去,但是Container 异常 + +

+

解决办法: +- 导入Unity内置Shader到项目中
+var arr = AssetDatabase.GetDependencies(path, true); 不能收集到内置Shader依赖。

+
    +
  • +

    Shader 中 UsePassFallBack及其他依赖需要单独处理
    +var arr = AssetDatabase.GetDependencies(path, true); 无法收集Shader引用依赖,但是如果shader打包bundle会自动手机依赖,造成引用计数错误,可能会造成冗余

    +
  • +
  • +

    变体收集(keywords),ShaderVariantCollection生成资产.shadervariants,然后收集整个项目的变体
    + 这里需要注意 ShaderVariantCollection.WarmUp,项目使用一个ShaderVariantCollection资产,会处理所有的变体,WarmUp执行可能需要更长时间

    +
  • +
+

Material 打包问题

+

m_SavedProperties的数据冗余,无用数据被打包进去 +

+
    +
  • 使用项目内工具 Tools/清理材质中的废弃属性 清理
  • +
+

打包冗余优化历程

+
+

AssetDatabase.GetDependencies无法获取unity内置shader的依赖关系,也不能获取Shader依赖其他Shader的依赖关系(FallBack ,UsePass等等)

+

EditorUtility.CollectDependencies() 可以获取内置Shader的依赖信息,而且能否收集shader依赖其他Shader的关系 (FallBack可以收集,UsePass需要测试)

+
+
    +
  1. +

    打包shader使用ShaderVariantCollection收集keyword组合,单独使用list引用所有要使用的shader,这时候发现会有unity 内置shader在list中,需要剔除,Standard变体太多,不能加入list中,不能被打包进去,需要将所有引用Standard的资源分析替换其他shader,()

    +
      +
    • 这里使用打包一个Shader 的AB包时通过IPreprocessShaders来收集所以keyword变体,
    • +
    +
  2. +
  3. +

    内置shader冗余(AssetDatabase.GetDependencies无法获取unity内置shader的依赖关系)

    +
      +
    • 导入内置Shader到项目中,然后替换所有内置shader引用,使用导入的shader替换原有内置shader,重新打包资源
    • +
    +
  4. +
  5. +

    默认材质球及内置Shader冗余,没有材质球资源的情况,这时候使用内置默认材质球(Default-Material,Sprites-Default,Default-ParticleSystem) + > IPreprocessShaders 可能无法处理默认材质球的shader问题,所以在上一步处理内置冗余时,默认材质球的引用没有收集到。

    +
      +
    • 使用工具找出所有使用Unity内置材质球的资源,根据需求替换其他材质球
    • +
    +
  6. +
  7. +

    材质球引用的纹理Texture 且Texture是Sprite资源,这时候Texture单独一个AB文件,材质球是另一个AB文件,则会出现冗余

    +
    +

    使用BuildPipeline.BuildAssetBundles打包AB文件会有这个问题 (图中下面绿色框,error文件夹

    +
    +
  8. +
  9. 使用UnityEditor.Build.Pipeline.CompatibilityBuildPipeline.BuildAssetBundles会解决这个问题(图中上面蓝色色框,general文件夹
    +

    参考 https://forum.unity.com/threads/unity-5-6-0b4-problem-with-asset-bundle-the-file-archive-cab-8bc5a956f7efa6356fcd1d00c8005f99.452006/

    +
    +
  10. +
+

+
    +
  1. +

    材质球m_SavedProperties中的无用属性(有可能引用纹理,但是已经删除了)

    +
    +

    上图中RenderTexture #4纹理,实际材质球中已经删除,但是还是会被引用

    +
    +
  2. +
  3. +

    完全替换内置资源

    +
    +

    https://blog.csdn.net/e295166319/article/details/60140796

    +
    +
  4. +
+

部队性能排查

+
+

物体缓存池 优先 +gc检查 +特效 +

+
+

+
    +
  • 部队头像update中刷新loadsprite和name (每帧调用)
      +
    • update中的业务分解,像loadsprite和name等不需要每帧执行的按需处理
    • +
    +
  • +
  • 部队英雄释放技能时对应头像播放UI特效,特效有内存泄露,特效创建后大概率不卸载,每次播放时不卸载的特效也跟着播放(缓存池失效)
      +
    • 使用缓存池处理,着重处理回收
    • +
    +
  • +
+
+

GC问题 +- String.Format

+
+
            //TODO:GC严重,调用频繁(一帧内调用多次)
+            view.m_lbl_count_troops_LanguageText.text =string.Format("{0}/{1}", count, m_TroopProxy.GetTroopDispatchNum());
+
+
    +
  • List或者Dictionary Clear后重建, 调用频繁
  • +
+

+
                case RssType.HolyLand_PVP:
+                case RssType.Sanctuary_PVP:
+                case RssType.Altar_PVP:
+                case RssType.Shrine_PVP:
+                case RssType.LostTemple_PVP:
+                case RssType.CheckPoint_PVP:
+                case RssType.Checkpoint_1_PVP:
+                case RssType.Checkpoint_2_PVP:
+                case RssType.Checkpoint_3_PVP:
+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git "a/ANote/Git\347\254\224\350\256\260/index.html" "b/ANote/Git\347\254\224\350\256\260/index.html" new file mode 100644 index 00000000..238b7ca3 --- /dev/null +++ "b/ANote/Git\347\254\224\350\256\260/index.html" @@ -0,0 +1,9360 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Git笔记 - 王国庆的博客 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + 跳转至 + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ +
+ + + + + + + + + +

Git笔记

+

Git LFS的使用

+
+

https://git-lfs.github.com/
+https://www.jianshu.com/p/493b81544f80
+https://zzz.buzz/zh/2016/04/19/the-guide-to-git-lfs/

+
+
# 查看安装的git lfs 环境信息
+git lfs env 
+git lfs env | head -2
+
+# 查看当前使用 Git LFS 管理的匹配列表
+git lfs track
+
+# 使用 Git LFS 管理指定的文件
+git lfs track "*.psd"
+
+# 不再使用 Git LFS 管理指定的文件
+git lfs untrack "*.psd"
+
+# 类似 `git status`,查看当前 Git LFS 对象的状态
+git lfs status
+
+# 枚举目前所有被 Git LFS 管理的具体文件
+git lfs ls-files
+
+# 检查当前所用 Git LFS 的版本
+git lfs version
+
+# 针对使用了 LFS 的仓库进行了特别优化的 clone 命令,显著提升获取
+# LFS 对象的速度,接受和 `git clone` 一样的参数。 [1] [2]
+git lfs clone https://github.com/user/repo.git
+
+
    +
  1. git lfs install开启lfs功能
  2. +
  3. +

    git lfs track "*.png" 添加大文件追踪,所有的png文件

    +
      +
    • 指定文件后缀名 git lfs track "*.filetype"
    • +
    • 指定某个目录下的所有文件 git lfs track "directory/*"
    • +
    • 具体指定某个文件 git lfs track "path/to/file"
    • +
    +
  4. +
  5. +

    git lfs track 查看所有的lfs文件追踪模式

    +
  6. +
  7. git lfs ls-files 可以显示当前跟踪的文件列表
  8. +
+

迁移已有的 git 仓库使用 git lfs 管理

+
# 重写 master 分支,将历史提交中的 *.zip 都用 git lfs 进行管理
+git lfs migrate import --include-ref=master --include="*.png"
+
+# 重写所有分支及标签,将历史提交中的 *.rar,*.zip 都用 git lfs 进行管理
+git lfs migrate import --everything --include="*.bin,*.lib,*.so,*.dll,*.a,*.param,*.zip,*.gz,*.png,*.jpg,*.unitypackage,*.hdr,*.HDR" 
+
+

注意: 重写历史后的提交需执行:

+
git push --force --all
+
+

注意: 如有迁移至 git lfs 前的仓库的多份拷贝,其他拷贝可能需要执行 git reset --hard origin/master 来重置其本地的分支,注意执行 git reset --hard 命令将会丢失本地的改动。

+

如果需要对本地仓库进行清理(非安全的)

+
git reflog expire --expire-unreachable=now --all
+git gc --prune=now
+
+

git clone命令

+

git clone -b ${branch} --depth=1 可以拉对应分支

+

git clone --depth=1后只有一个分支的解决办法

+
+

git clone --depth=1克隆项目后,使用git branch -r查看远程仓库只显示一个分支,实际远程仓库有多个分支。

+

这种方法克隆的项目只包含最近的一次commit的一个分支,体积很小,即可解决文章开头提到的项目过大导致Timeout的问题,但会产生另外一个问题,他只会把默认分支clone下来,其他远程分支并不在本地,也看不到其他远程分支。

+
+

checkout 一个新的分支

+
$ git clone --depth 1 https://github.com/dogescript/xxxxxxx.git
+$ git remote set-branches origin 'remote_branch_name'
+$ git fetch --depth 1 origin remote_branch_name
+$ git checkout remote_branch_name
+
+
+

参考:https://blog.csdn.net/qq_29094161/article/details/120649473

+
+

获取全部分支

+

修改.git/config文件中的
+fetch = +refs/heads/master:refs/remotes/origin/master
+为 +fetch = +refs/heads/*:refs/remotes/origin/*
+然后执行

+

git fetch --all

+
+

参考https://blog.csdn.net/a924068818/article/details/115725982

+
+

Git更新远程分支列表

+
git remote update origin --prune
+
+
+

这个命令没有测试

+
+

Git 强制推送

+
git push -f origin master
+git push --force origin master:master
+
+

Git Pull

+

git pull origin master:mm

+

在repository中找到名字为master的branch,使用它去更新local repository中找到名字为mm的branch,,如果local repository下不存在名字是mm的branch,那么新建一个。(如果mm不是当前激活的分支,git pull执行完也不是)

+

Git Push

+

git push origin master:master

+
git push origin master:master
+
+

在local repository中找到名字为master的branch,使用它去更新remote repository下名字为master的branch,如果remote repository下不存在名字是master的branch,那么新建一个

+

Git Clean

+
+

git clean命令用来从你的工作目录中删除所有没有tracked过的文件

+

git clean经常和git reset --hard一起结合使用. 记住reset只影响被track过的文件, 所以需要clean来删除没有track过的文件. 结合使用这两个命令能让你的工作目录完全回到一个指定的的状态

+
+
    +
  • +

    git clean -n + 是一次clean的演习, 告诉你哪些文件会被删除. 记住他不会真正的删除文件, 只是一个提醒

    +
  • +
  • +

    git clean -f + 删除当前目录下所有没有track过的文件. 他不会删除.gitignore文件里面指定的文件夹和文件, 不管这些文件有没有被track过

    +
  • +
  • +

    git clean -f <path> + 删除指定路径下的没有被track过的文件

    +
  • +
  • +

    git clean -df + 删除当前目录下没有被track过的文件和文件夹

    +
  • +
  • +

    git clean -xf + 删除当前目录下所有没有track过的文件. 不管他是否是.gitignore文件里面指定的文件夹和文件

    +
  • +
+

下面的例子要删除所有工作目录下面的修改, 包括新添加的文件. 假设你已经提交了一些快照了, 而且做了一些新的开发

+
git reset --hard
+
+git clean -df
+
+

运行后, 工作目录和缓存区回到最近一次commit时候一摸一样的状态.
+git status会告诉你这是一个干净的工作目录, 又是一个新的开始了!

+
+

https://www.jianshu.com/p/0b05ef199749 + ## Git Config

+
+

用git config命令查看配置文件:
+- 查看仓库级的config,即.git/.config,命令:git config –local -l
+- 查看全局级的config,即~\.gitconfig,命令:git config –global -l
+- 查看系统级的config,即C:\Program Files\Git\etc\gitconfig,命令:git config –system -l
+查看当前生效的配置,命令:git config -l,这个时候会显示最终三个配置文件计算后的配置信息

+

查看当前生效的配置,命令:git config -l,这个时候会显示最终三个配置文件计算后的配置信息,如下图:

+

git有几个配置文件呢? 是的,聪明的你,稍微查查资料就知道咯,git里面一共有3个配置文件 +- 仓库级配置文件:.git/config (该文件位于当前仓库下)

+
[core]
+    repositoryformatversion = 0
+    filemode = true
+    bare = false
+    logallrefupdates = true
+    ignorecase = true
+    precomposeunicode = true
+[remote "origin"]
+    url = git@github.com:codingriver/codingriver.github.io.git
+    fetch = +refs/heads/*:refs/remotes/origin/*
+[branch "master"]
+    remote = origin
+    merge = refs/heads/master
+[lfs]
+    repositoryformatversion = 0
+
+
    +
  • 全局配置文件 ~/.gitconfig
  • +
+
[user]
+    name = codingriver
+    email = codingriver@163.com
+[filter "lfs"]
+    clean = git-lfs clean -- %f
+    smudge = git-lfs smudge -- %f
+    process = git-lfs filter-process
+    required = true
+
+
+
+
    +
  • 系统级配置文件 C:\Program Files\Git\etc\gitconfig (git安装目录)
  • +
+
[diff "astextplain"]
+    textconv = astextplain
+[filter "lfs"]
+    clean = git-lfs clean -- %f
+    smudge = git-lfs smudge -- %f
+    process = git-lfs filter-process
+    required = true
+[http]
+    sslBackend = openssl
+    sslCAInfo = C:/Program Files/Git/mingw64/ssl/certs/ca-bundle.crt
+[core]
+    autocrlf = false
+    fscache = true
+    symlinks = false
+[pull]
+    rebase = false
+[credential]
+    helper = manager-core
+[credential "https://dev.azure.com"]
+    useHttpPath = true
+[init]
+    defaultBranch = master
+
+
+

sh + git config --global user.name “yourname” + git config --global user.email “email@email.com ” + 案例: + sh + git config --global user.name "codingriver" + git config --global user.email "codingriver@163.com"

+

## Git 提交空文件夹

+

git设计时是不支持空文件夹提交的,这里是在文件夹里面新建.gitignore文件或者.gitkeep空文件来处理的

+
+

unity也支持忽略以.开头的文件的

+
+

新建.gitignore文件 +在空文件夹下新建.gitignore文件,文件内容:

+
## Ignore everything in this directory
+*
+## Except this file
+!.gitignore
+
+

这样就能提交git仓库了 +我这是在windows上操作的,不能直接创建以.开头的文件,参考这篇文章Windows创建.开头的文件或者.开头的文件夹

+

新建.gitkeep文件 +在空文件夹下新建.gitkeep文件,是空文件,这样就能提交git仓库了

+

shell判断git remote是否存在

+

假如,要判断的 remote 的名字为 faraway。 +- 第一种方案

+
if git config remote.faraway.url > /dev/null; then 
+ …
+fi
+
+
    +
  • 第二种方案
  • +
+
if git remote | grep faraway > /dev/null; then 
+ …
+fi
+
+

判断 remote 是否已存在,如果已存在,直接进行 git fetch faraway 等操作,如果不存在,git add remote faraway http://xxxxx 后,再进行操作。

+
+

参考:
+https://www.jianshu.com/p/e4b9c6c6bab7
+https://www.cnblogs.com/ibingshan/p/11126345.html
+https://blog.csdn.net/longerzone/article/details/12948925

+
+

shell 判断 git branch 分支是否存在

+

举例:判断当前分支是否为master,本地分支master是否存在,远端分支master是否存在

+
if git branch --list master | grep "*"; then 
+    ## 分支名字完全匹配,不是模糊匹配!只能匹配master,不能匹配mastertest等
+    echo "当前分支是master"
+    git reset --hard
+    git clean -df > /dev/null    
+elif git branch --list | grep -w master > /dev/null; then
+    ## 分支名字完全匹配,不是模糊匹配!只能匹配master,不能匹配mastertest等
+    echo "master不是当前分支,但本地分支master存在"
+    git reset --hard
+    git clean -df > /dev/null
+    git checkout  master
+elif git branch -r | grep -w master; then
+    ## 分支名字完全匹配,不是模糊匹配,只能匹配origin/master,不能匹配origin/mastertest等
+    echo "本地分支master不存在,远端master分支存在"
+    git reset --hard
+    git clean -df > /dev/null
+    git checkout -b  master origin/master
+else
+    echo "本地分支master不存在,远端master分支也不存在"
+fi
+
+
+

shell 判断 git本地工作文件夹是否干净(clean)

+
if [[ -z $(git status -s) ]]; then
+  ## 没有修改的文件和未纳入版本控制的文件(untracked)的文件
+  echo "没有可提交内容"
+  exit 2
+fi
+
+
+

https://blog.csdn.net/10km/article/details/100689481

+
+

git文件内容没变但status显示不同的解决方案

+

+

这里提示的不同,是文件的权限改变了。

+

SO,解决方案奏是:不让git检测文件权限的区别

+
git config core.filemode false
+
+
+

参考: https://blog.csdn.net/u012109105/article/details/51252242

+
+

The file will have its original line endings in your working directory

+

git 报错:

+
warning: LF will be replaced by CRLF in basic-markdown-syntax/index.html.
+The file will have its original line endings in your working directory
+warning: LF will be replaced by CRLF in hugo-loveit-problems/index.html.
+The file will have its original line endings in your working directory
+
+

解决方案:

+
    git rm -r --cached .
+    git config core.autocrlf false
+
+    git add .
+
+
+

https://blog.csdn.net/namechenfl/article/details/81257973

+
+

Git处理换行符问题

+

对仓库使用 .gitattributes文件进行配置

+
# Set the default behavior, in case people don't have core.autocrlf set.
+* text=auto #让git自行处理左边匹配的文件使用何种换行符格式,这是默认选项。
+# text eol=lf
+
+# Explicitly declare text files you want to always be normalized and converted
+# to native line endings on checkout.
+*.c text
+*.h text
+
+# Declare files that will always have CRLF line endings on checkout.
+*.sln text eol=crlf
+
+# Denote all files that are truly binary and should not be modified.
+*.png binary
+*.jpg binary
+
+
+

参考: https://blog.csdn.net/github_30605157/article/details/56680990

+
+

项目在用的.gitattributes文件

+
* text=auto
+
+*.cs diff=csharp text
+*.cginc text
+*.shader text
+*.spriteatlas text eol=lf
+# Unity YAML
+*.anim -text merge=unityyamlmerge diff
+*.asset -text merge=unityyamlmerge diff
+*.controller -text merge=unityyamlmerge diff
+*.mat -text merge=unityyamlmerge diff
+*.meta -text merge=unityyamlmerge diff
+*.physicMaterial -text merge=unityyamlmerge diff
+*.physicsMaterial2D -text merge=unityyamlmerge diff
+*.prefab -text merge=unityyamlmerge diff
+*.unity -text merge=unityyamlmerge diff
+
+# Unity LFS
+*.cubemap filter=lfs diff=lfs merge=lfs -text
+*.unitypackage filter=lfs diff=lfs merge=lfs -text
+
+# 2D formats
+*.[aA][iI] filter=lfs diff=lfs merge=lfs -text
+*.[bB][mM][pP] filter=lfs diff=lfs merge=lfs -text
+*.[dD][dD][sS] filter=lfs diff=lfs merge=lfs -text
+*.[eE][xX][rR] filter=lfs diff=lfs merge=lfs -text
+*.[gG][iI][fF] filter=lfs diff=lfs merge=lfs -text
+*.[hH][dD][rR] filter=lfs diff=lfs merge=lfs -text
+*.[iI][fF][fF] filter=lfs diff=lfs merge=lfs -text
+*.[jJ][pP][eE][gG] filter=lfs diff=lfs merge=lfs -text
+*.[jJ][pP][gG] filter=lfs diff=lfs merge=lfs -text
+*.[pP][iI][cC][tT] filter=lfs diff=lfs merge=lfs -text
+*.[pP][nN][gG] filter=lfs diff=lfs merge=lfs -text
+*.[pP][sS][dD] filter=lfs diff=lfs merge=lfs -text
+*.[tT][gG][aA] filter=lfs diff=lfs merge=lfs -text
+*.[tT][iI][fF] filter=lfs diff=lfs merge=lfs -text
+*.[tT][iI][fF][fF] filter=lfs diff=lfs merge=lfs -text
+
+# 3D formats
+*.3[dD][mM] filter=lfs diff=lfs merge=lfs -text
+*.3[dD][sS] filter=lfs diff=lfs merge=lfs -text
+*.[aA][bB][cC] filter=lfs diff=lfs merge=lfs -text
+*.[bB][lL][eE][nN][dD] filter=lfs diff=lfs merge=lfs -text
+*.[cC]4[dD] filter=lfs diff=lfs merge=lfs -text
+*.[cC][oO][lL][lL][aA][dD][aA] filter=lfs diff=lfs merge=lfs -text
+*.[dD][aA][eE] filter=lfs diff=lfs merge=lfs -text
+*.[dD][xX][fF] filter=lfs diff=lfs merge=lfs -text
+*.[fF][bB][xX] filter=lfs diff=lfs merge=lfs -text
+*.[jJ][aA][sS] filter=lfs diff=lfs merge=lfs -text
+*.[lL][wW][oO] filter=lfs diff=lfs merge=lfs -text
+*.[lL][wW][oO]2 filter=lfs diff=lfs merge=lfs -text
+*.[lL][wW][sS] filter=lfs diff=lfs merge=lfs -text
+*.[lL][xX][oO] filter=lfs diff=lfs merge=lfs -text
+*.[mM][aA] filter=lfs diff=lfs merge=lfs -text
+*.[mM][aA][xX] filter=lfs diff=lfs merge=lfs -text
+*.[mM][bB] filter=lfs diff=lfs merge=lfs -text
+*.[oO][bB][jJ] filter=lfs diff=lfs merge=lfs -text
+*.[pP][lL][yY] filter=lfs diff=lfs merge=lfs -text
+*.[sS][kK][pP] filter=lfs diff=lfs merge=lfs -text
+*.[sS][tT][lL] filter=lfs diff=lfs merge=lfs -text
+*.[zZ][tT][lL] filter=lfs diff=lfs merge=lfs -text
+
+# Audio formats
+*.[aA][iI][fF] filter=lfs diff=lfs merge=lfs -text
+*.[aA][iI][fF][fF] filter=lfs diff=lfs merge=lfs -text
+*.[bB][aA][nN][kK] filter=lfs diff=lfs merge=lfs -text
+*.[iI][tT] filter=lfs diff=lfs merge=lfs -text
+*.[mM][oO][dD] filter=lfs diff=lfs merge=lfs -text
+*.[mM][pP]3 filter=lfs diff=lfs merge=lfs -text
+*.[oO][gG][gG] filter=lfs diff=lfs merge=lfs -text
+*.[sS]3[mM] filter=lfs diff=lfs merge=lfs -text
+*.[wW][aA][vV] filter=lfs diff=lfs merge=lfs -text
+*.[xX][mM] filter=lfs diff=lfs merge=lfs -text
+
+# Video formats
+*.[aA][sS][fF] filter=lfs diff=lfs merge=lfs -text
+*.[aA][vV][iI] filter=lfs diff=lfs merge=lfs -text
+*.[fF][lL][vV] filter=lfs diff=lfs merge=lfs -text
+*.[mM][oO][vV] filter=lfs diff=lfs merge=lfs -text
+*.[mM][pP]4 filter=lfs diff=lfs merge=lfs -text
+*.[mM][pP][eE][gG] filter=lfs diff=lfs merge=lfs -text
+*.[mM][pP][gG] filter=lfs diff=lfs merge=lfs -text
+*.[oO][gG][vV] filter=lfs diff=lfs merge=lfs -text
+*.[wW][mM][vV] filter=lfs diff=lfs merge=lfs -text
+
+# Build
+*.[dD][lL][lL] filter=lfs diff=lfs merge=lfs -text
+*.[mM][dD][bB] filter=lfs diff=lfs merge=lfs -text
+*.[pP][dD][bB] filter=lfs diff=lfs merge=lfs -text
+*.[sS][oO] filter=lfs diff=lfs merge=lfs -text
+
+# Packaging
+*.7[zZ] filter=lfs diff=lfs merge=lfs -text
+*.[bB][zZ]2 filter=lfs diff=lfs merge=lfs -text
+*.[gG][zZ] filter=lfs diff=lfs merge=lfs -text
+*.[rR][aA][rR] filter=lfs diff=lfs merge=lfs -text
+*.[tT][aA][rR] filter=lfs diff=lfs merge=lfs -text
+*.[tT][aA][rR].[gG][zZ] filter=lfs diff=lfs merge=lfs -text
+*.[zZ][iI][pP] filter=lfs diff=lfs merge=lfs -text
+
+# Fonts
+*.[oO][tT][fF] filter=lfs diff=lfs merge=lfs -text
+*.[tT][tT][fF] filter=lfs diff=lfs merge=lfs -text
+
+# Documents
+*.[pP][dD][fF] filter=lfs diff=lfs merge=lfs -text
+
+# Collapse Unity-generated files on GitHub
+*.asset             linguist-generated
+*.mat               linguist-generated
+*.meta              linguist-generated
+*.prefab            linguist-generated
+*.unity             linguist-generated
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git "a/ANote/IGG\345\274\200\345\217\221\347\254\224\350\256\260/index.html" "b/ANote/IGG\345\274\200\345\217\221\347\254\224\350\256\260/index.html" new file mode 100644 index 00000000..4341c760 --- /dev/null +++ "b/ANote/IGG\345\274\200\345\217\221\347\254\224\350\256\260/index.html" @@ -0,0 +1,8701 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + IGG开发笔记 - 王国庆的博客 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + 跳转至 + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+ +
+ + + + + + + + + +

IGG开发笔记

+

待分析

+ +

PureMVC

+
+

说明:https://blog.csdn.net/wangjiangrong/article/details/107686954
+官方文档:http://puremvc.org/docs/PureMVC_IIBP_Chinese.pdf
+github c#: https://github.com/PureMVC/puremvc-csharp-standard-framework
+github: https://github.com/PureMVC
+官网:http://puremvc.org (提供pdf中文文档)

+
+
+

如果在 Mediator 里有很多对 View Component 的操作(响应 Event 或Notification),那么应该考虑将这些操作封装为 View Component 的一个方法,提高可重用性。 +如果一个 Mediator 有太多的对 Proxy 及其数据的操作,那么,应该把这些代码重构在 Command 内,简化 Mediator,把业务逻辑(Business Logic)移放到Command 上,这样 Command 可以被 View 的其他部分重用,还会实现 View 和Model 之间的松耦合提高扩展性

+
+
+
+

网络通信优化

+
+

游戏内通信协议使用Sproto序列化

+
+

通信反序列化分析

+
+

优化前:
+当前截图为win10 unity editor测试
+编号为1的测试数据,每帧解析30001协议50次的开销,测试数据为Map_ObjectInfo.request,数据包含有60个MapObjectInfo对象
+

+
+

协议开销分析 +1. SocketAsyncEventArgs mReceiveEventArgs 异步socket 收到网络数据 +1. 网络数据 写入mReceiveBuffer中 + > +1. 然后CopymReadQueueBuffer(网络数据拆包)进行传递 + > +1. 然后使用packetData进行派发,packetData是新的buffer + >
+ >
+ > +1. 通过Socket异步回调线程将packetData 存储到private LockFreeQueue<MemoryStream> mReceivePacketQueue队列中,然后通过unity主线程进行派发,在主线程中进行Decode + > +1. Decode时,MemoryStream.ToArray()进行拷贝,生成新的buffer + >
+ >
+ >
+ >
+ >
+1. Decode首先进行解压缩,解压缩时先把原始bufferCopynewBuffernewBuffer是新的buffer + > +1. 然后进行解压缩decomBuffer,解压缩decomBuffer是新的buffer + > + > +1. 解压后进行解密outBuffer,解密是新的buffer + > +1. 解密后进行解包unpack_data,解包是新的buffer + > +1. 解包后解析对象Package +1. 解析对象后进行解析对象GateMessage,GateMessage对象有新的byte[]字段,该字段有新的buffer + > 收到的每个协议先反序列化成GateMessage对象,该对象的字段private List<MessageContent> _content; 带有private byte[] _networkMessage;字段,这个字段是真实协议对象的原始数据,gc严重 + > + > +1. 将解析对象封装到RpcInfo对象中 +1. 使用GateMessage进行解包unpack_data,解包是新的buffer +1. 开始解析具体业务对象,同时会封装到RpcInfo中 +1. RpcInfo进行解封,使用具体业务对象进行业务派发

+

通信反序列化优化

+
    +
  1. 调整Sproto反序列化流程,使其支持缓存重复利用,将MessageContentbyte[]调整为BufferStream,支持重复利用buffer,减少GC
  2. +
  3. packetDataMemoryStream 替换成RecyclableMemoryStream,进行缓存利用,且支持跨线程处理
  4. +
  5. +

    调整业务,替换掉MemoryStream.ToArray(),解析过程使用RecyclableMemoryStream

    +
  6. +
  7. +

    解压缩前使用的decomBuffer,替换为常驻的大buffer,只创建一次,就不销毁了,重复利用

    +
  8. +
  9. 解压缩后使用的decomBuffer,替换为常驻的大buffer,只创建一次,就不销毁了,重复利用
  10. +
  11. 解密后使用的outBuffer,替换为常驻的大buffer,只创建一次,就不销毁了,重复利用
  12. +
  13. 解包使用的unpack_data,这里使用两个大buffer进行处理,只创建一次,就不销毁了,重复利用, + > 解析协议GateMessage前执行unpack,解析协议GateMessage后,解析具体业务协议前也会执行unpack,这里剥离具体业务的影响,进行分步处理,使用两个大buffer分别对应两次解析协议
  14. +
  15. RpcInfoGateMessage.requestGateMessage.responseMessageContent等等对象需要进缓存池,这些对象是为反序列化服务的
  16. +
  17. 调用频繁的业务协议使用缓存池进行处理 + >MessageContent是工具生成的,为了防止冲突,改为MessageContentEx
    + >
  18. +
+
+

优化中期参照:
+编号为1的测试数据,每帧解析30001协议50次的开销,测试数据为Map_ObjectInfo.request,数据包含有60个MapObjectInfo对象
+
+上面结果是将所有协议都做缓存,包括MapObjectInfo内部字段引用的协议 +
+上面是将MapObjectInfoEntityDictionary使用MapObjectInfo,不创建新的,具体业务会有bug +et.heros = new System.Collections.Generic.Dictionary<System.Int64,SprotoType.BattleHeroInfo>(); +

+

优化最终结果: +编号为1的测试数据,每帧解析30001协议50次的开销,测试数据为Map_ObjectInfo.request,数据包含有60个MapObjectInfo对象
+
+MapObjectInfo的内部字段引用的协议类型没有做缓存(多个协议会引用相同的协议,复杂度太高);
+MapObjectInfoEntityDictionary还是创建新的,否则业务有bug;

+

测试数据优化前后GC对比 +编号为1的测试数据,每帧解析30001协议50次的开销,测试数据为Map_ObjectInfo.request,数据包含有60个MapObjectInfo对象
+优化前 15.9MB +优化后 4.8MB

+


+游戏中优化前后对比:
+优化前每一次调用 GC255kb +优化后第一次调用 GC114.1kb +优化后第二次调用 GC86.6kb

+

对进入地图场景初始化的Map_ObjectInfo.request协议数据50倍测试 +优化前 15.9MB +优化后 4.8MB +

+

PoolMgr的缓存池测试

+

+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git "a/ANote/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/index.html" "b/ANote/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/index.html" new file mode 100644 index 00000000..0700adf7 --- /dev/null +++ "b/ANote/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/index.html" @@ -0,0 +1,9108 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + IGG性能分析笔记 - 王国庆的博客 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + 跳转至 + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ +
+ + + + + + + + + +

IGG性能分析笔记

+
+

世界地图优化前的数据
+

+

优化后的数据:
+

+
+

动态批处理说明

+
+

参考 : +Unity批处理手游——动态VS静态批处理
+Unity3d Tips +Batch Breaking Cause
+Unity-25种合批失败的原因

+
+

+

+

unity版本不同,动态合批限制也不同,需根据对应版本的官方文档去处理。

+

(注意:如果您正在开发 Unity PC 游戏,请记住动态批处理会产生 CPU 开销,因此除非您受到繁重的 GPU 渲染时间的瓶颈,否则您可能希望在项目中禁用动态批处理!)

+

注意:动态批处理并不总是适用于场景中的所有对象!即使您设法将所有对象减少到 300 个顶点以下,大量动态批处理计算的成本最终也会成为非常昂贵的 CPU 操作。(虽然 GPU 会很高兴!)

+

与静态批处理不同,动态批处理的对象可以四处移动,并且具有刚体物理特性。他们也有自己的局限性:

+
    +
  • 必须与批处理的比例完全相同(也不支持镜像比例,例如,如果您有一个比例为 -1 的对象与一个比例为 1 的对象)(Sprite RendererParticle System不受缩放影响)
  • +
  • 需要共享完全相同的材料参考以一起批处理
  • +
  • 必须按顺序一起渲染!如果渲染顺序在一个批次之间有其他渲染,那么它将被分成多个批次
  • +
  • 每个对象都必须低于上述顶点限制,具体取决于它使用的着色器
  • +
+

常见合批失败原因:

+

+

Unity中Player Settings > Other Settings > Dynamic Batching

+

+

注意: Sprite RendererParticle System 默认启用Dynamic Batching,不会受到 Player Setting的动态合批开关的影响。

+

地图GPU优化规划

+
    +
  1. 场景内地表,灯光,河流等
  2. +
  3. 场景内圣地,关卡及周边环境等静态业务对象
  4. +
  5. 场景内的静态特效及圣地关卡的静态特效
  6. +
  7. 运行时玩家城堡,联盟建筑,资源点等 动态创建后位置不动的纹理使用图集进行合批,待定
  8. +
  9. 怪物,召唤怪(艾萨克),圣地召唤怪等,(核心处理普通怪物,量级太大)
  10. +
  11. 玩家部队
  12. +
  13. 部队上挂载的特效,战斗的弹道,弹道特效等
  14. +
  15. 战斗的部队释放技能特效(暂不处理,周期太长,效率提升有限)
  16. +
+

优化方案简述

+
    +
  1. 世界地图上,所有单位及装饰物进行渲染顺序规划,提高动态合批的成功率
  2. +
  3. +

    简化模式 + >- 部队简化显示
    + >- 部队上的默认特效进行移除
    + >- 性能瓶颈时,考虑部分动画进行屏蔽出现动画,消失动画,休闲动画)
    + >- 不同的图集使用不同的sortingorder进行渲染顺序控制,保证动态合批成功率(放弃前后遮挡关系,效率优先)
    + >- 部队阵型及移动插值计算进行简化,特别是移动方向变化后阵型调整的计算(转向)

    +
  4. +
  5. +

    怪物合批
    + >- 不同的图集使用不同的sortingorder进行渲染顺序控制,保证动态合批成功率(放弃前后遮挡关系,效率优先)
    + >- 性能瓶颈时,考虑部分动画进行屏蔽出现动画,消失动画,休闲动画)
    + >- 怪物上的默认特效进行移除 + >- 部队阵型及移动插值计算进行简化,特别是移动方向变化后阵型调整的计算(转向)

    +
  6. +
+

优化结果:

+
    +
  1. 在展示层(不计算UI) 优化前drawcall为452,优化后为71
  2. +
  3. 在lod第一层时 优化前drawcall为107,优化后为87
  4. +
  5. 在lod最高层时 所有建筑单位(重点是关卡圣地)的drwacall为54,优化后为17
  6. +
  7. 战斗部队200支头像 优化前drawcall为1400,优化后为22
  8. +
  9. 驻扎部队23支的名字 优化前drawcall为46,优化后为13
  10. +
+

渲染顺序规划

+

地图渲染顺序说明(New) MapSortingOrder

+

默认层-Default(地面,山,河流,水,树草等)

+
        Land = -100,                       //地表
+        MountainGrove = -90,      // 山 树  
+        RiverLake = -80,                //河水 湖泊
+        HolyLandRoad = -70,       //圣地表 公路
+        Circle = -1,        // 圆形的环 当移动到obj,长按空地/主城时出现
+        行军线
+        联盟线
+        资源富集区
+
+

怪物层-Monster
+角色层-Army 默认是和建筑一个层级,在性能瓶颈时,单独一个配置,方便部队合批 +建筑层-Building (城堡,资源点,联盟建筑,圣地,关卡,帮会据点等) +特效层-Effect(战斗特效,静态特效) +BillText +3DUI特效层-UIEffect +天空层-Sky(迷雾等)

+

待处理问题

+
    +
  1. lod中间层,会显示缩略图,还有展示图(自由行军会用)
  2. +
+

怪物合批和部队合批

+

地图 建筑单位可以合并图集处理 +

+

分析场景 map-analyse.unity

+

分析前的drawcall478 +

+

只显示怪的数据drawcall274 +

+

怪是3转3的图片,带阴影,有8个方向,每个动作都有8个方向,所有动作:攻击,待机,行走,跑步,攻击,休闲

+
    +
  1. +

    调整图集方案 +现在图集的方案是每只怪的所有动作作为一个图集
    +调整图集:
    +所有怪的相同动作作为一个图集,因为图集太大,这里将所有士兵怪按照相同动作生成为一个图集的方案去处理

    +
  2. +
  3. +

    使用精灵切块工具SpriteDicing +尝试将序列帧中每帧相同部分进行公用。

    +
  4. +
  5. +

    一个动作需要为8方向生成序列帧,这里考虑下是否能接受 左右镜像来减少序列帧素材, + > 提供左上,左,左下的序列帧,通过镜像方式作为右上,右,右下的序列帧使用,这样就能减少3方向的素材,减小包体 + >需要考虑阴影单独拆分

    +
  6. +
  7. +

    动态合并图集

    +
  8. +
  9. +

    shader实现一个材质球代表一个怪物的展示(shader实现几*几的方格子,同时渲染多个图集的数据) + > + >弊端:怪物和别的怪或者部队有交叉重叠时,不能交叉渲染,前后遮挡关系会有细节问题

    +
  10. +
  11. +

    怪物里面的每个士兵和英雄 根据图集设置固定的order,分离渲染顺序 + >会丢失阵型前后遮挡关系,也会丢失重叠怪的遮挡关系

    +
    +

    测试调整后的怪,drawcall只有7 +

    +
    +
  12. +
  13. +

    角色和角色阴影分离 + > 减小角色图集 ,阴影可以使用更低的分辨率,使角色图集能容纳更多角色的图 + > 降低overdraw,角色和阴影在一个图中,形成大面积的透明区域,overdraw严重 + > 可以根据角色生成对应的mesh,降低overdraw,带阴影的情况下mesh不能复用 + > 阴影可以再中低平台设备上关闭,或者中端使用一个阴影图片来代替,降低阴影的性能开销,更可控

    +
  14. +
  15. 使用 SortingGroup 进行分组 + >Unity 按 Sorting Layer 和 Order in Layer 渲染器属性对同一排序组中的所有渲染器进行排序。在此排序过程中,Unity 不会考虑每个渲染器的 Distance to Camera 属性。实际上,Unity 会根据包含 Sorting Group 组件的根游戏对象的位置,为整个排序组(包括其所有子渲染器)设置 Distance to Camera 值。
    + >配置在怪物上,表现一般,大量重叠的对象上效果应该更好一些
    + >
  16. +
+

结论

+

使用第6条方案,每个士兵和英雄 根据图集设置固定的order,分离渲染顺序

+
+

数据比对 测试场景 mapbatches.unity +优化前的怪和部队drawcall 426
+

+

优化后的怪和部队drawcall 36
+

+
+

场景内lod高层静态特效及物体

+

+

+
+

优化后的数据 (lod 对象这里没有进行图集整合,需要时可以支持) +优化前drawcall是 54,优化后是17,效果非常可观,但需要注意动态合批的CPU及内存开销 +

+
+

静态特效

+

+

优化后特效后的结果:

+

+

结论 +1. 给特效物体不同的材质球指定不同的sortingorder

+
+

如果不同材质球的物体使用相同的sortingorder,unity内部 +

+
+

静态物体

+

+drawcall 序号1和3是相同图集相同材质球,序号2和4也是相同图集相同材质球,1,2,3,4使用的相同shader,且,1,2,3,4动态合批的物体互相没有遮挡,

+

结论

+
    +
  1. +

    动态合批时,渲染顺序穿插会造成消耗更多的drawcall,最好指定固定的sortingorder,手动控制顺序,这样能提高动态合批的成功率.

    +
    +

    调整后的drawcall从4个变成2个

    +
    +
  2. +
  3. +

    所有圣地的图片需要调整到一个图集里面

    +
    +

    调整后的drawcall从4个变成1个

    +
    +
  4. +
  5. +

    占领后需要调整颜色,则使用MaterialPropertyBlock进行处理,不会打断合批(前提是支持GPU Instancing)

    +
  6. +
  7. +

    这里使用顶点颜色进行颜色调整

    +
  8. +
  9. +

    SpriteRenderer针对不同sprite生成不同的mesh,mesh不同,所以不支持GPU Instancing合批,

    +
  10. +
+

在lod层时 会有展示层内容占用drawcall

+

相机从展示层到lod1层时,策划的需求是可点击业务单位及可以自由行军到业务单位,所以展示层的节点保留了,会把展示层的sprite节点localScale设置为0,但scale为0还是会占用drawcall。

+

处理方案: 将scale为0时的sprite位置设置到相机外的位置

+

+

处理后的drawcall

+

+

静态特效

+
+

联盟标记,城堡护盾,冒烟特效,着火特效, 部队移动烟尘,lod高层圣地常驻特效,普攻子弹特效,

+
+
    +
  1. 城堡护盾、冒烟,着火特效 +护盾特效(effect_map_huzhao)本身占用10个drawcall,有两个模型组成,每个模型5个材质球,且模型不能动态合批合批(因为不同材质球的渲染顺序相同) + +
  2. +
+

+7个护盾特效,占用60个drawcall,视野内随着护盾数量的增长,drawcall也急速增长

+

结论 +- 建议优化护盾特效,降低材质球数量 + > 这里半球护盾特效是有两个mesh组成(1/2的半球mesh),这两个mesh是相同的,(思考下是否可以使用1/4的半球mesh?) + > eff_banyuan_007_1eff_banyuan_007_2 mesh是一样的,资源冗余问题 + > +- 将材质球的RenderQueue进行调整,不同的材质球使用不同的值,可以进行动态合批 + >测试后的drawcall 11 + > + >将 Sorting Group组件移除后drawcall 是6Sorting Group这里打断动态合批了) + >

+
    +
  1. 普通子弹特效
  2. +
+

爆炸特效 +effect_map_tongyong_touzhiwu_baozha 炮弹爆炸特效 有7个材质球,每个爆炸特效 占用7个drawcall。 +24个爆炸特效,同时播放会有 71 drawcall,特效有动态合批,但drawcall还是随着特效数量增长而增长

+

处理方案:将爆炸特效的sortingorder分别设置不同的值,24个爆炸特效,同时播放会有9个drawcall(一个clear的drawcall,一个天空盒的drawcall),特效完美动态合批,且drawcall不会随着特效数量增加而增加

+
+

着火和冒烟特效 也是这样的处理方案

+
+

炮弹特效

+

+ +effect_map_tongyong_touzhiwu 炮弹的顶点数量过多不能动态合批, +处理方案:调整模型的顶点数,不高于300个顶点

+

UI及UI特效

+
    +
  1. UI_Pop_TroopNameState 部队名字及状态图标
  2. +
+

+

23支部队,只显示UI_Pop_TroopNameState 占用drawcall就达到46 +UI_Pop_TroopNameState节点上添加有Canvas组件,所以打断合批了

+

+

处理方案: + - 将Canvas组件移除,给状态图标添加SortingGroup组件,将名字和图标层级分离,这样保证Text组件和Image组件分别独立合批,不会被打断 + - 图标显示分底图和图标,将底图和图标放到同一个图集中。

+
+

优化后: +

+
+
    +
  1. UI_Pop_TroopSelectHUD 部队选中后的信息显示
  2. +
+

5支部队,占用20个drawcall +UI_Pop_TroopSelectHUD节点上添加有Canvas组件,所以打断合批了 +

+
+

关于合批 UGUI的一个缺陷
+ +头像框合批失败,所以是3个drawcall
+ +白色图片不能合批,一共占用3个drawcall + +根据上图占用3个drawcall,推断出,Hierachy内的上下关系不能保证临近位置合批,受到前后遮挡关系影响。 +这里有个问题是 第1个drawcall先绘制的左侧的白图,根据Hierachy的顺序,应该先绘制蓝色图片才对? +UGUI是根据Depth进行相邻的Depth合批,不相邻的不能合批,这里可以调整节点关系

+
+
    +
  1. UI_Pop_TroopFightInfoHUD 战斗头像
  2. +
+

10支部队,占用50个drawcall,200支部队占用drawcall为1000 ,200支部队副将头像也显示 drawcall为 1400 +
+200支部队
+
+问题:
+- 每个头像上添加有Canvas组件

+
+

去除Canvas组件后 drawcall 是19,200支部队占用drawcall为375
+- 每个头像上的指向线添加有Canvas组件
+这个Canvas组件是保证指向线的层级在头像后面,不能去除。
+这里去除Canvas后使用固定的材质球调整Render Queue来替代处理层级关系,替换材质球后drawcall为16,200支部队占用drawcall为412
+
+- 如果不显示指向线,且把Canvas组件去除,头像的drawcall为9, 200支部队占用drawcall为175
+
+- 指向线通过MultiImage组件合并成一个mesh进行处理
+这时候指向线和头像同时显示的drawcall为10, 200支部队占用drawcall为176
+

+
+

处理方案 +- 指向线通过MultiImage组件合并成一个mesh进行处理

+
    +
  • +

    战斗头像超过一定数量限制后,处理办法:

    +
      +
    1. 进行位置更新频率调整,
    2. +
    3. 缩放效果停止播放, + > 别人不会播放,只有自己的部队及和自己对战的会播放
    4. +
    5. 战斗头像的技能特效停止播放,战斗抖动效果停止播放 + > 别人不会播放,只有自己的部队及和自己对战的会播放
    6. +
    7. 分离成名字组和头像组,遮挡关系舍弃
    8. +
    9. 数量级很大时,再次分组,canvas Rebuild更新Mesh会非常耗时
    10. +
    +
  • +
  • +

    图集调整,将英雄战斗头像这一个尺寸类型单独打包一个图集为duilietouxiang图集

    +
      +
    1. 将战斗头像框,血条,技能充能条放到该图集中
    2. +
    3. 部队的状态图标,状态图标底图,别动队图标放到该图集中(战斗头像和行军列表使用相同的头像) + >
      +

      英雄头像的图集放有多种尺寸类型的,这个图集不合理
      +

      +
      +
    4. +
    +
  • +
+

优化前 200支部队,不显示指向线时 drawcall为1200
+ 优化后 200支部队,不显示指向线时 drawcall为 21 (去掉canvs,所有头像相关图片调整到一个图集中)
+

+

内城优化

+

+每个hud上添加有Canvas组件
+移除后的效果: +

+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git "a/ANote/Linux\347\254\224\350\256\260/index.html" "b/ANote/Linux\347\254\224\350\256\260/index.html" new file mode 100644 index 00000000..61a97942 --- /dev/null +++ "b/ANote/Linux\347\254\224\350\256\260/index.html" @@ -0,0 +1,8967 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Linux笔记 - 王国庆的博客 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + 跳转至 + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ +
+ + + + + + + + + +

Linux笔记

+

常用命令

+
    +
  • 路由表 +在 Windows 系统中,你可以使用以下命令来查看路由表:
  • +
+
route print
+
+或者
+
+netstat -r
+
+#  tracert显示路由耗时
+tracert www.example.com
+或者
+tracert 192.168.1.1
+
+

在 Unix/Linux 系统中,你可以使用以下命令来查看路由表:

+
netstat -rn
+或者
+
+ip route
+
+

这些命令会列出当前系统的路由表,包括目的网络、子网掩码、网关、接口等信息,这对于网络故障排查和理解数据包如何在网络中传输非常有用。

+

zerotier配置及moon服务器搭建

+
+

https://www.itblogcn.com/article/1815.html

+

https://www.cnblogs.com/cx850116/p/17805450.html

+
+

Moon是什么,为什么需要Moon?

+

ZeroTier通过自己的多个根服务器帮助我们建立虚拟的局域网,让虚拟局域网内的各台设备可以打洞直连。这些根服务器的功能有些类似于通过域名查询找到服务器地址的DNS服务器,它们被称为Planet。然而这里存在一个非常严重的问题,就是Zerotier的官方行星服务器都部署在国外,从国内访问的时候延迟很大,甚至在网络高峰期的时候都没法访问,这也会导致我们的虚拟局域网变得极不稳定,经常掉链子。

+

为了应对网络链接的延迟和不稳定,提高虚拟局域网的速度和可靠性,Zerotier允许我们建立自己的moon卫星中转服务器。

+

作为Moon服务器不需要具备太强大的CPU性能/内存空间和存储空间,虚拟机、VPS、或者云服务器甚至一个树莓派都行,当然,这台服务器需要长时间可靠在线并且具有静态IP地址(ZeroTier官网上说公网IP或者内网IP都可以,只是如果用的是内网IP的话,在外网的设备就只能依靠Planet而不能使用moon了)。

+

Moon服务器配置过程

+

以Linux系统为例配置Moon服务器

+
TIPS:
+

由于ZeroTier本身使用UDP协议,因此如果存在防火墙的话,需要开放UDP,否则无法连接。 +本篇内容仅介绍ZeroTier-Moon服务器的配置。

+
安装
+

下载并安装ZeroTier:

+
curl -s https://install.zerotier.com/ | sudo bash
+
+

安装完成后得到一个ID:

+
*** Success! You are ZeroTier address [ 1c110b9ac2 ]
+
+

加入网络(只作为Moon服务的话不用加入网络):

+
sudo zerotier-cli join 3efa5cb78a961967
+200 join OK
+
+

进入ZeroTier的默认安装目录,生成Moon配置文件:

+
cd /var/lib/zerotier-one
+sudo zerotier-idtool initmoon identity.public > moon.json
+vim moon.json
+
+

其内容包括id、objtype、roots、signingKey等等。

+

生成的文件样式如下:

+
 "id": "1c110b9ac2",
+ "objtype": "world",
+ "roots": [
+  {
+   "identity": "9c960b9ac2:0:daca38dfc5f3",
+   "stableEndpoints": []
+  }
+ ],
+ "signingKey": "676f0c29eb8d6f2f00ce22ee2082b3ec",
+ "signingKey_SECRET": "39de9f7ab16d0adb035276b7281f73344",
+ "updatesMustBeSignedBy": "676f0c29eb8d6f2f00ce22ee",
+ "worldType": "moon"
+}
+
+

这里我们需要根据自己服务器的公网静态IP,修改stableEndpoints那一行格式如下,其中11.22.33.44为你的公网IP,9993是默认的端口号:

+
"stableEndpoints": [ "11.22.33.44/9993" ]
+
+

根据moon.json文件生成真正需要的签名文件.moon:

+
sudo zerotier-idtool genmoon moon.json
+
+

输出:

+
wrote 0000009c960b9ac2.moon (signed world with timestamp 1280398410930)
+
+

执行该命令以后会在软件目录下生成一个类似000000xxxxxxxxx.moon的文件,妥善保存该文件,因为要使用moon服务器,必须在所有客户端上面都发送一个这个文件。

+

移动.moon签名文件到moons.d目录下并且重启服务

+
cd /var/lib/zerotier-one
+mkdir moons.d
+mv 000000*.moon moons.d
+service zerotier-one restart
+
+

其他客户端使用

+

其他机器如果要使用Moon服务器,必须要在本地加入之前生成的.moon签名文件并重启服务才能生效。有两种方法。

+

一、在本机的Zerotier安装目录创建moons.d文件夹,然后下载该签名文件放在创建的moons.d目录里,重启服务。

+
#For linux
+# 一般是 /var/lib/zerotier-one 目录下
+cd /var/lib/zerotier-one
+mkdir moons.d
+# 将 .moon 文件移动到 moons.d 目录下
+
+
+

二、直接使用命令zerotier-cli orbit

+
Windows和Linux通用
+# zerotier-cli orbit
+sudo zerotier-cli orbit 9c960b9ac2 9c960b9ac2
+200 orbit OK
+# zerotier-one restart
+sudo service zerotier-one restart
+zerotier-one stop/waiting
+zerotier-one start/running, process 18347
+
+

客户端添加.moon文件后需要重启ZeroTier One!Windows要在服务中重启ZeroTier One

+
检查连接是否成功
+
#for linux and windows(windows需要用管理员模式启动cmd输入)
+zerotier-cli listpeers
+
+

如果输出中出现一条最后为MOON的记录,说明已经成功连接Moon服务器

+
zerotier-cli listpeers
+200 listpeers <ztaddr> <path> <latency> <version> <role>
+200 listpeers id myip/9993;6012;1706 -1 1.8.4 MOON
+200 listpeers 62f865ae71 50.7.252.138/9993;6012;1070 -294 - PLANET
+
+

centos更改docker镜像默认存储位置

+

1、需求背景 +docker镜像默认存放在根目录下,而有时候根目录往往比较小或者有时候需要重装系统,将docker镜像放在根目录下有被删除或者根目录被撑爆的风险,因此需要将docker镜像默认存储位置更改为其他数据盘的位置。

+

2、解决办法

+

2.1、修改/etc/docker/daemon.json文件 +在终端执行以下命令:

+
vim /etc/docker/daemon.json
+
+

然后添加以下内容:

+
{
+  "data-root": "/data/docker" #将docker的默认存储位置在该目录下
+}
+
+

2.2、重启docker

+
systemctl restart docker
+
+

2.3、检测是否生效

+
docker info | grep "Docker Root Dir"
+
+

如果输出为 Docker Root Dir: /data/docker,则说明更改生效

+

如果感觉对你有用,麻烦点击一下小红心,谢谢!

+

CentOS8.5安装docker

+
+

https://blog.csdn.net/Monster_WangXiaotu/article/details/122590389

+

https://www.cnblogs.com/jolyonyue/p/16963568.html

+
+

Centos安装docker和docker-compose

+

CentOS9以上 +1. yum安装docker服务

+
yum install -y docker
+
+
    +
  1. 启动docker服务
  2. +
+
systemctl start docker  
+systemctl enable docker
+systemctl status docker
+
+
    +
  1. 安装docker-compose
  2. +
+
curl -L https://github.com/docker/compose/releases/download/v2.14.1/docker-compose-$(uname -s)-$(uname -m) -o /usr/local/bin/docker-compose
+或者: curl -L http://mirror.azure.cn/docker-toolbox/linux/compose/v2.14.1/docker-compose-$(uname -s)-$(uname -m) -o /usr/local/bin/docker-compose
+
+chmod +x /usr/local/bin/docker-compose
+
+
    +
  1. 开启你的docker之旅吧!
  2. +
+
docker run ...
+
+

Emulate Docker CLI using podman. Create /etc/containers/nodocker to quiet msg. Error: open /proc/sel

+

问题描述:在Centos8系统中,使用docker run时,出现如下报错: +Emulate Docker CLI using podman. Create /etc/containers/nodocker to quiet msg. +Error: open /proc/self/uid_map: no such file or directory

+

解决办法: +1,卸载podman软件(可以使用rpm -qa|grep docker) +yum remove docker +2,下载docker-ce源 +curl https://download.docker.com/linux/centos/docker-ce.repo -o /etc/yum.repos.d/docker-ce.repo +3,安装docker-ce +yum install docker-ce -y

+

问题原因分析: +Centos 8使用yum install docker -y时,默认安装的是podman-docker软件

+

问题解决:Failed to download metadata for repo ‘appstream‘: Cannot prepare internal mirrorlist:...

+

大家都知道Centos8于2021年年底停止了服务,大家再在使用yum源安装时候,出现下面错误“错误:Failed to download metadata for repo ‘AppStream’: Cannot prepare internal mirrorlist: No URLs in mirrorlist”

+

1、进入yum的repos目录

+
cd /etc/yum.repos.d/
+
+

2、修改所有的CentOS文件内容

+
sed -i 's/mirrorlist/#mirrorlist/g' /etc/yum.repos.d/CentOS-*
+
+sed -i 's|#baseurl=http://mirror.centos.org|baseurl=http://vault.centos.org|g' /etc/yum.repos.d/CentOS-*
+
+

3、更新yum源为阿里镜像

+
wget -O /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-vault-8.5.2111.repo
+
+yum clean all
+
+yum makecache
+
+

4、yum安装测试是否可以yum安装

+
yum install wget –y
+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git "a/ANote/NAS\347\254\224\350\256\260/index.html" "b/ANote/NAS\347\254\224\350\256\260/index.html" new file mode 100644 index 00000000..158a1213 --- /dev/null +++ "b/ANote/NAS\347\254\224\350\256\260/index.html" @@ -0,0 +1,8800 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NAS笔记 - 王国庆的博客 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + 跳转至 + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ +
+ + + + + + + + + +

NAS笔记

+

照片备份方案

+

moments

+
+

官网: https://github.com/kingwrcy/moments

+

部署后登录不上去

+
+

PhotoPrism

+
+

https://www.photoprism.app
+https://github.com/photoprism/photoprism
+需要付费 pro版本

+
+

PhotoSync

+
+

+
+

Nextcloud

+
+

移动端体验太差

+
+

Pho

+
+

官网: https://pho.tools/

+
+

Rclone

+
+

官网: https://rclone.org/

+
+

负责把Alist的webdav映射到NAS的文件系统内,来把照片喂给PhotoPrism

+

MTPhotos

+
+

官网: https://mtmt.tech/

+
+

永久收费99元

+

群晖Photos

+

影音(媒体)中心方案

+
+

https://fairysen.com/788.html

+
+

方案1

+

qBittorrent + Jackett + Radarr & Sonarr + ChineseSubFinder + Jellyfin + Jellyseerr

+

方案2

+

qBittorrent + Jackett + NASTool + ChineseSubFinder + Jellyfin

+

方案3

+

qBittorrent + Movie Robot + ChineseSubFinder + Jellyfin

+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git "a/ANote/PBR\347\254\224\350\256\260/index.html" "b/ANote/PBR\347\254\224\350\256\260/index.html" new file mode 100644 index 00000000..7f05da10 --- /dev/null +++ "b/ANote/PBR\347\254\224\350\256\260/index.html" @@ -0,0 +1,9796 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + PBR笔记 - 王国庆的博客 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + 跳转至 + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+ +
+ + + + + + + + + +

PBR笔记

+

什么是PBR

+

PBR即基于物理的渲染过程。

+

PBR 并不是“一项”技术,它是由一系列技术的集合

+

满足以下条件的光照模型才能称之为PBR光照模型: +1. 基于微平面模型(Be based on the microfacet surface model) +1. 能量守恒(Be energy conserving) +1. 使用基于物理的BRDF(Use a physically based BRDF)

+
+

参考:https://learnopengl-cn.github.io/07%20PBR/01%20Theory/
+PBR,或者用更通俗一些的称呼是指基于物理的渲染(Physically Based Rendering),它指的是一些在不同程度上都基于与现实世界的物理原理更相符的基本理论所构成的渲染技术的集合。正因为基于物理的渲染目的便是为了使用一种更符合物理学规律的方式来模拟光线,因此这种渲染方式与我们原来的Phong或者Blinn-Phong光照算法相比总体上看起来要更真实一些。除了看起来更好些以外,由于它与物理性质非常接近,因此我们(尤其是美术师们)可以直接以物理参数为依据来编写表面材质,而不必依靠粗劣的修改与调整来让光照效果看上去正常。使用基于物理参数的方法来编写材质还有一个更大的好处,就是不论光照条件如何,这些材质看上去都会是正确的,而在非PBR的渲染管线当中有些东西就不会那么真实了。

+

虽然如此,基于物理的渲染仍然只是对基于物理原理的现实世界的一种近似,这也就是为什么它被称为基于物理的着色(Physically based Shading) 而非物理着色(Physical Shading)的原因。

+
+

基于物理渲染的优点

+
    +
  • 很容易就可以作出真实和照片级的效果
  • +
  • 同一配置可以适用于在不同HDR光照环境下
  • +
  • 接口简单而直观,都是基于世界真实的参数。(如粗糙度,金属度,等等)
  • +
  • 不需要美术去提供经验性的”奇怪”参数和配置
  • +
  • 更容易去解决问题和扩展需求
  • +
+

基于物理的渲染和传统区别

+
    +
  • 有着基于物理规则的光照模式
  • +
  • 有着无处不在的fresnel效果 (指当光到达两种材质的接触面时,一些光在接触面的表面被反射出去,而另一部分光将发生折射穿过接触面)
  • +
  • 能量守恒“energy conservation”,物体平面的反射光无法超过它所解接受的入射光
  • +
  • 基于物体材质,会分辨金属和介电质,微平面的概念
  • +
  • 线性空间的光照,支持gamma矫正的,HDR渲染和tonemap
  • +
+

PBR特征

+

这节阐述的是PBR呈现的效果特征,而非底层物理原理的特征。 +相比传统的Lambert着色和Phong着色,PBR着色在效果上有着质的提升,可以表示更多更复杂的材质特征: +- 表面细节 +- 物体粗糙度 +- 区别明显的金属和绝缘体 +- 物体的浑浊程度 +- 菲涅尔现象:不同角度有不同强度的反射光 +- 半透明物体 +- 多层混合材质 +- 清漆效果 +- 其它更复杂的表面特征

+

+

Phong模型着色效果,只能简单地表现理想模型的漫反射和高光,渲染出的效果跟真实世界相差甚远。

+

+

PBR材质效果球,它们真实地渲染出各类材质的粗糙、纹理、高光、清漆、边缘光等等表面细节特征。PBR对渲染效果真实感的提升可见一斑。

+

PBR和PBS的关系

+

PBR(Physically Based Rendering)是一种渲染方式(基于物理渲染),它使用的材质是PBS(Physically Based Shader),中文名:基于物理的渲染技术(基于物理着色)。可以对光和材质之间的行为进行更加真实的建模。PBS只考虑材质在真实物理环境下应该有的效果。PBR包围的范围会更广一些,比如GI/AO/SUN等复杂情况,这些东西加上PBS,才是PBR。

+

Unity中的PBS即是把PBR算法封装起来,只用修改PBS的参数就能达到PBR的效果。

+

在Unity中,PBS分为两类,一个叫做Standard,一个叫做Standard(Specular Setup),我们把它称为标准着色器的高光版,它们共同组成了一个完整的PBS光照明模型,而且非常易于使用。

+

光与物质的交互(light and matter)

+

光是一种横向传播的电磁波,电磁波的波长范围非常广,但只有390~760nm之间的一段波谱是人眼可见到的,也就是在图形学里对渲染着色起作用的部分。另外因为光有波粒二象性的缘故,有时候我们在图形学里也会把光做为光子(photon)来处理。

+

+

当光投射到传感器(眼睛,照相机等)上时,颜色和亮度就会被吸收并感知,而光与物质交互后被感知的,就是物体的颜色。

+

物质对光的影响,可以用折射率(refractive index)来描述,当由复数来表示折射率时,它的实部影响速度(相对于真空中的速度),虚部来影响光的衰减(被吸收),折射率会改变光的波长。

+

我们先根据物质按照内部折射率是否均匀来进行分类: +- 均匀介质(Homogeneous Media)---直线传播 + 内部密度相同的物质,意味着他有唯一的折射率,对应透明的均匀物质来讲(如水,玻璃),光通过时,并不会改变光的颜色或强度,而当物质对某一种可见光谱有吸收率的时候,那么,光就会随着在物质内的传播距离而逐渐被吸收,而光的方向并不发生改变,这就是光被吸收(absorption)。

+
光穿透玻璃和水时,方向、颜色和强度都不会发生:
+
+![](image/PBR笔记/2021-12-07-15-31-14.png)
+
+如果物质对某一种可见光谱有吸收率时,光方向不会发生变化,而光的强度会随着距离丧失强度(改变颜色),也就是光被吸收了:
+
+![](image/PBR笔记/2021-12-07-15-31-37.png)
+
+
    +
  • +

    非均匀介质(heterogeneous medium)---散射 + 当非均匀物质内部的折射率变化斜率很大时(突变),就会发生散射(Scattering)现象,光会被分割为多个方向,但光的总量并不会发生变化:

    +

    +
  • +
+

除了以上两种交互外,物质还可能因为其他能量发出新的光,称为自发光“emission”。

+

光与物质交互的三种方式:吸收、散射、自发光
+示意图如下:
+

+
+

散射(Scattering): 由观察尺度划分 +- 漫反射(Diffuse): 观察像素大于散射距离 +- 次表面散射(Subsurface Scattering): 观察像素小于散射距离 +- 透射(Transmission): 入射光经过折射穿过物体后的出射现象, 为次表面散射的特例

+
+

另外,折射率缓慢的逐渐变化不会导致光线的分离,而是导致其传播路径的弯曲。 当空气密度因温度而变化时,通常可以看到这种效果,例如海市蜃楼(mirages)和热形变(heat distortion)。见下图。

+

+

光与平面的交互(light and object surface)

+

上面我们讲了光在密度不同的物质内传递的现象,而渲染中最为典型的,是发生在物体表面时,关于光与空气和物质之间的散射效果。 +这个时候平面散射光会分为两部分:进入平面的部分(折射,在物体内部传播中被吸收或散射),从平面出去的部分(反射);一个假设完美无限光学平坦的平面(简称光学平面)反射效果,平面两侧的空气和物体有各自的折射率:

+

+

但实际上,平面大多都不是光学平面(除了镜子或镜头等),而是一种微几何体(Microgeometry),表面都会有一些比可见光波长要大的不规则凹凸,但又小到无法覆盖一个像素或者采样点,所以,我们就把这种非光学平面,理解为一组微小光学平面的集合,而可见光的反射,实际上是在平面各个点上轻微不同方向的反射集合,也就是后面会提到的微平面理论(Microsurface Theory)。

+

+

Fresnel(菲涅尔)反射

+
+

Fresnel表现的是材质的反射率和入射角也就是光源入射向量和平面法线向量的夹角)的对应关系 +- 表示观察看到的反射光线的向量与视角相关的现象 +- 光线以不同角度入射会有不同反射率, 掠射角度(90)下反射率最大 +- F0: 平行于表面法向量入射的菲涅尔反射值, 任意角度的菲涅尔反射可由 F0 和入射角度计算得出 +- 光线对于不同物质有不同的反射率, 非金属 F0: 0.02~0.04, 金属 F0: 0.7~1.0 +- 光线入射角越大,镜面反射的比例越大

+

如果要达到真实的fresnel,那么美术对fresnel的控制应该越少越好,需要提供一些参数值来使用,通常是光泽度(Gloss,或粗糙度 Roughness)和反射率(Reflectivity)。提供一个基础反射率,来设置平面最小的反射值,让fresnel曲线从这个最小反射值开始,以满足不同角度的需求。

+
+

次级表面散射(Subsurface Scattering)

+

菲涅尔方程定义的是在不同观察方向上,表面上被反射的光除以被折射的光的比例。我们通过该方程可以计算得到被反射的光的能量,根据能量守恒我们就可以得知被折射进物体的光的能量了。那些折射进物体的光也没闲着,他们在物体内进行着称之为次表面散射(Subsurface Scattering)的行为,最终会看似随机地射出表面。如下图所示:

+

+

其中黄色出射光为直接反射光,红色出射光为次表面反射光。可以看到,在一个平滑的平面上(即不考虑微平面理论),在宏观上来说,反射光即为材质的高光 specular 部分,而次表面反射因为是完全随机出射,所以构成了漫反射 diffuse 部分。

+

这里还是得提一句 Subsurface Scattering 的注意事项。先从引擎玩起再来补图形学知识的人可能会疑惑,Subsurface Scattering 不是做皮肤那种,模拟光从一个像素点入射,从另一个像素点出射的物理现象吗?但这里整篇文章模拟的都是一个像素所覆盖的平面所产生的物理属性(还是之前提到的 pixel footprint 的概念),而这种跨越多个像素的、宏观的次表面反射会在后面使用一个单独的物理模型去模拟,即 BSSRDF。

+
+

当像素小于出射到入射距离时,每个点的着色就会收到其他光入射到其他点的影响,也就是常说的“次级表面散射”技术,很重要的一点是,它和普通的漫反射着色是一种物理现象(都是折射光的次级表面散射),唯一不同的就是散射的距离与观察点大小的关系,一个通常被认为是“次级表面散射”的表现,当在较远的距离观察时,就可以被认为是漫反射着色(例如远距离角色的皮肤),而“正规的漫反射着色”在很近距离观察时,也会有次级表面散射的效果。

+
+

微平面理论(Microfacet Theory)

+
+

上面描述的反射和散射都依赖于表面的朝向。放大来看,这被用来渲染网格的形状,也可以使用法线贴图来描述更小的细节。这样任何渲染系统都可以处理更多的细节,把反射和散射表现得更好。

+

然而,任然有一点没有考虑到。大多数真实世界的表面都存在非常小的缺口:凹槽、裂缝、凸块,这些都因为太小了以至于眼睛无法看到,并且使用正常解析度的法线贴图都无法表现出来。尽管无法被肉眼看到,但这些微粒还是影响着反射和折射。

+

+

为表面细节最容易在反射中被观察到(散射并不会被太多的影响到,这里不会讨论到)。上图中,入射光的平行线当从粗糙的表面反射是发生了交叉,因为每一条射线遇到的表面方向都不一样。就像将球抛向墙面一样,如果墙面非常不平整,球任然会反弹,但是反弹方向是不可预测的。简而言之,表面越粗糙,越多的反射光线会产生交叉,看上去越“模糊”。

+

不幸的是,为了着色而评估每一个微表面特征对美术、内存、计算量来说都是非常昂贵的操作。我们该怎么做呢?事实证明,如果我们放弃直接描述微表面,取而代之使用整体的粗糙度,就可以写出相当精确的着色器来产生类似的效果。这个值通常指的是光泽度(Gloss)平滑度(Smoothness)或者粗糙度(Roughness)。可以从纹理或者一个常量值中获取。

+

这种微表面细节对于任何材质来说都是非常重要的,因为真实世界到处都是各种各样的微表面特征。光泽贴图并不是一个新的盖面,但是它在基于物理的渲染中起到了关键的作用,因为微表面细节对光反射有着很大的影响。就如我们一会儿会看到的,有几个关于微表面属性基于物理渲染改善的注意事项。

+
+
    +
  • 将物体表面建模成无数微观尺度上有随机朝向的理想镜面反射的微表面(microfacet)
  • +
  • 实际 PBR 工作流中, 微平面通过粗糙度贴图高光贴图来表示
  • +
  • 微观尺度上, 表面越粗糙反射越模糊, 越光滑反射越集中
  • +
  • 到达微观尺度后任何平面都可以用称为微平面的细小镜面来进行描绘
  • +
  • 用统计学的方法估算微平面粗糙程度
      +
    • 半角向量(Halfway Vector): 位于光线向量 l 和视线向量 v 之间的中间向量 + > h=normalize(lightDir+viewDir);
    • +
    • 用基于一个平面的粗糙度(Roughness)来计算出半程向量的方向与微平面平均取向方向一致的概率
    • +
    +
  • +
+

微平面的取向方向与半程向量的方向越是一致,镜面反射的效果就越是强烈越是锐利。然后再加上一个介于0到1之间的粗糙度参数(Roughness),这样我们就能概略的估算微平面的取向情况了:

+

+

我们可以看到,较高的粗糙度值显示出来的镜面反射的轮廓要更大一些。与之相反地,较小的粗糙值显示出的镜面反射轮廓则更小更锐利。

+

Surface Reflectance

+

前文已经提到了微几何体,之所以有微平面的概念,因为从宏观来看,在我们渲染模型网格时,使用法线贴图就可以描述表面小的细节,但这样仍然会有一定的缺失,很多真实世界的平面上,还是有一些微小的凹陷,裂缝或突起,而用肉眼是很难看清楚的,小到连正常大小的法线贴图也无法来表现,虽然肉眼无法看到,但这些微观特征,还是对Diffuse和Specular产生了影响。

+

微平面的细节,对反射的影响更多,也就是Specular,因为粗糙的微平面会把反射光分散或者内部遮挡,所以有了下面两个项目来描述这个现象: +- Normal Distribution Function(法线分布函数) + 因为微几何体的所有平面的方向,并不是均匀分布的,如果是分布比较均匀的光滑平面,那么光就会在几乎相同的方向反射,产生清晰的高光,如果粗糙表面则是模糊的高光.

+
有多少微平面点的法线更倾向宏观平面的法线方向,我们把这种平面法线方向分布的统计,称之为microgeometry normal distribution function **D()**,和fresenl方程不同的是,**D()** 并没有一个类似0~1的范围,而是来帮助确定微平面法线在某一个给定方向上的集中度。
+
+所以,D()决定了高光的镜面反射高光的大小,亮度和形状,一些D()会提供前面提到的类似”**Roughness(粗糙度)**”的参数(也可以是glossiness),当粗糙度降低时,微几何体平面的法线方向就会更集中在宏观的平面法线方向上,D()的值也会变高。除了指定粗糙度参数外,也可以通过传递一张Glossn map的方式提供更高的细节。
+
+
    +
  • +

    Geometry Function(几何函数)

    +
      +
    • +

      shadowing现象 + 因为微几何体的构造缘故,一些入射光的平面点被内部遮挡,成为了内部阴影而无法接受光照(也就不能反射光)。

      +
    • +
    • +

      masking现象 + 而有一些反射光被内部遮挡,他们的反射光无法被观察到,虽然有反射光可以多次反弹后再被视点观察到,但在微平面理论里可以忽略不计了。

      +
    • +
    +

    正因为有这种现象,所以需要有一个Geometry Function G(),来代表反射光的可见度,所以G()是在0~1之间的一个范围值,在着色模型里,有时会和其他参数合并称为V()(Visiblity)。和D()一样,因为微平面有凹凸感,当它的粗糙度提高时,shadow和masking的现象也会增加,粗糙度高的平面会光滑平面更阴暗一些,G()也要收到roughness参数的影响。另外G()也是下面要讲的能量守恒的一个基础,它使得反射光不会高于平面的入射光。

    +
  • +
+

能量守恒

+

PBR是如何实现近似的能量守恒呢?

+

为了回答这个问题,先弄清楚镜面反射(specular)和漫反射(diffuse)的区别。

+

一束光照到材质表面上,通常会分成反射(reflection)部分和折射(refraction)部分。反射部分直接从表面反射出去,而不进入物体内部,由此产生了镜面反射光。折射部分会进入物体内部,被吸收或者散射产生漫反射。

+

折射进物体内部的光如果没有被立即吸收,将会持续前进,与物体内部的微粒产生碰撞,每次碰撞有一部分能量损耗转化成热能,直至光线能量全部消耗。有些折射光线在跟微粒发生若干次碰撞之后,从物体表面射出,便会形成漫反射光。

+

+

照射在平面的光被分成镜面反射和折射光,折射光在跟物体微粒发生若干次碰撞之后,有可能发射出表面,成为漫反射。

+

通常情况下,PBR会简化折射光,将平面上所有折射光都视为被完全吸收而不会散开。而有一些被称为 次表面散射(Subsurface Scattering) 技术的着色器技术会计算折射光散开后的模拟,它们可以显著提升一些材质(如皮肤、大理石或蜡质)的视觉效果,不过性能也会随着下降。

+

金属(Metallic)材质会立即吸收所有折射光,故而金属只有镜面反射,而没有折射光引起的漫反射。

+

回到能量守恒话题。反射光与折射光它们二者之间是互斥的,被表面反射出去的光无法再被材质吸收。故而,进入材质内部的折射光就是入射光减去反射光后余下的能量。

+

根据上面的能量守恒关系,可以先计算镜面反射部分,此部分等于入射光线被反射的能量所占的百分比。而折射部分可以由镜面反射部分计算得出。

+
float kS = calculateSpecularComponent(...); // 反射/镜面 部分
+float kD = 1.0 - ks;                        // 折射/漫反射 部分
+
+

通过以上代码可以看出,镜面反射部分与漫反射部分的和肯定不会超过1.0,从而近似达到能量守恒的目的。

+

反射方程(Reflectance Equation) ( 渲染方程 )

+

渲染方程(Render Equation)是用来模拟光的视觉效果最好的模型。而PBR的渲染方程是用以抽象地描述PBR光照计算过程的特化版本的渲染方程,被称为反射方程

+

PBR的反射方程可抽象成下面的形式:

+

+
+

参数说明: +- fr ( p , ωi , ω0 ) : 双向反射分布函数(BRDF) +- Li ( p , ωi ) : 灯光颜色*灯光强度(radiance=lightColor*attenuation) +- n⋅ωi : dot(N,L) +- ωi : 光源方向(L) +- ω0 : 视线方向(V) +- p : 当前点(或者是任意一个点 +- n : 法线(N)

+
+

反射方程计算了点𝑝在所有视线方向𝜔0上被反射出来的辐射率𝐿0(𝑝,𝜔0)的总和。换言之:𝐿0计算的是在𝜔i方向的眼睛观察到的𝑝点的总辐照度。

+
+
+

反射方程看似很复杂,但如果拆分各个部分加以解析,就可以揭开其神秘的面纱。

+

为了更好地理解反射方程,先了解辐射度量学(Radiometry)。辐射度量学是一种用来度量电磁场辐射(包括可见光)的手段。有很多种辐射度量(radiometric quantities)可以用来测量曲面或者某个方向上的光,此处只讨论和反射方程有关的一种量,它就是辐射率(Radiance),用𝐿来表示。

+

先用一个表展示辐射度量学涉及的概念、名词、公式等信息,后面会更加详细地介绍。

+

+
    +
  • +

    辐射通量(Radiant Flux):辐射通量Φ表示的是一个光源所输出的能量,以瓦特为单位。

    +
  • +
  • +

    立体角(Solid Angle):立体角用ω表示,它可以为我们描述投射到单位球体上的一个截面的大小或者面积。投射到这个单位球体上的截面的面积就被称为立体角(Solid Angle),你可以把立体角想象成为一个带有体积的方向:
    +
    +可以把自己想象成为一个站在单位球面的中心的观察者,向着投影的方向看。这个投影轮廓的大小就是立体角。

    +
  • +
  • +

    辐射强度(Radiant Intensity):辐射强度(Radiant Intensity)表示的是在单位球面上,一个光源向每单位立体角所投送的辐射通量。举例来说,假设一个全向光源向所有方向均匀的辐射能量,辐射强度就能帮我们计算出它在一个单位面积(立体角)内的能量大小:
    +
    +计算辐射强度的公式如下所示:
    +
    +(其中I表示辐射通量Φ除以立体角ω)。

    +
  • +
+
+

在理解了辐射通量,辐射强度与立体角的概念之后,我们终于可以开始讨论辐射率的方程式了。这个方程表示的是,一个拥有辐射强度Φ的光源在单位面积A单位立体角ω上的辐射出的总能量:
+

+
+
    +
  • +

    辐射率:是辐射度量学上表示一个区域平面上光线总量的物理量,它受到入射(Incident)(或者来射)光线与平面法线间的夹角θ的余弦值cosθ的影响:当直接辐射到平面上的程度越低时,光线就越弱,而当光线完全垂直于平面时强度最高。cosθ就直接对应于光线的方向向量和平面法向量的点积: + c + float cosTheta = dot(lightDir, N); + 辐射率方程很有用,因为它把大部分我们感兴趣的物理量都包含了进去。如果我们把立体角ω和面积A看作是无穷小的,那么我们就能用辐射率来表示单束光线穿过空间中的一个点的通量。这就使我们可以计算得出作用于单个(片段)点上的单束光线的辐射率,我们实际上把立体角ω转变为方向向量ω然后把面A转换为点p。这样我们就能直接在我们的着色器中使用辐射率来计算单束光线对每个片段的作用了。

    +

    事实上,当涉及到辐射率时,我们通常关心的是所有投射到点p上的光线的总和,而这个和就称为辐射照度或者辐照度(Irradiance)。在理解了辐射率和辐照度的概念之后,让我们再回过头来看看反射率方程:
    +

    +
  • +
+

我们知道在渲染方程中L代表通过某个无限小的立体角ωi在某个点p的辐射率,而立体角可以视作是入射光方向向量ωi。将用来衡量入射光与平面法线夹角对能量的影响的cos𝜃分量移出辐射率方程,作为反射方程的单独项𝑛⋅𝜔i

+

反射方程计算了点𝑝在所有视线方向𝜔0上被反射出来的辐射率𝐿0(𝑝,𝜔0)的总和。换言之:𝐿0计算的是在𝜔i方向的眼睛观察到的𝑝点的总辐照度。

+

反射方程里面使用的辐照度,必须要包含所有以𝑝点为中心的半球Ω内的入射光,而不单单只是某一个方向的入射光。这个半球指的是围绕面法线𝑛的那一个半球:
+

+
+

笔者注:为什么只计算半球而不计算整个球体呢?

+

因为另外一边的半球因与视线方向相反,不能被观察,也就是辐射通量贡献量为0,所以被忽略。

+
+

为了计算这个区域(半球)内的所有值,在反射方程中使用了一个称作为积分的数学符号 ∫,来计算半球 Ω 内所有的入射向量𝑑𝜔i

+

积分计算面积的方法,有解析(analytically)和渐近(numerically)两种方法。目前尚没有可以满足渲染计算的解析法,所以只能选择离散渐近法来解决这个积分问题。

+

具体做法是在半球Ω按一定的步长将反射方程离散地求解,然后再按照步长大小将所得到的结果平均化,这种方法被称为黎曼和(Riemann sum)

+

至此,反射方程中,只剩下𝑓𝑟项未描述。𝑓r就是双向反射分布函数(Bidirectional Reflectance Distribution Function, BRDF),它的作用是基于表面材质属性来对入射辐射度进行缩放或者加权。

+

什么是BRDF

+

双向反射分布函数(Bidirectional Reflectance Distribution Function,BRDF)是一个使用入射光方向𝜔i作为输入参数的函数,输出参数为出射光𝜔0,表面法线为𝑛,参数𝑎表示的是微平面的粗糙度。

+

更笼统地说,它描述了入射光线经过某个表面反射后如何在各个出射方向上分布这可以是从理想镜面反射到漫反射、各向同性或者各向异性的各种反射。

+

BRDF函数是近似的计算在一个给定了属性的不透明表面上每个单独的光线对最终的反射光的贡献量。假如表面是绝对光滑的(比如镜子),对于所有入射光𝜔i的BRDF函数都将会返回0.0,除非出射光线𝜔𝑜方向的角度跟入射光线𝜔i方向的角度以面法线为中轴线完全对称,则返回1.0。

+

怎么实现BRDF

+

BRDF有好几种模拟表面光照的算法,然而,基本上所有的 实时渲染管线使用的都是Cook-Torrance BRDF

+

Cook-Torrance BRDF分为 漫反射镜面反射 两个部分:
+
+其中𝑘𝑑是入射光中被折射的比例,𝑘𝑠是另外一部分被镜面反射的入射光。BRDF等式左边的𝑓𝑙𝑎𝑚𝑏𝑒𝑟𝑡表示的是漫反射部分,这部分叫做伦勃朗漫反射(Lambertian Diffuse)。它类似于我们之前的漫反射着色,是一个恒定的算式:

+


+其中𝑐代表的是Albedo或表面颜色,类似漫反射表面纹理。除以𝜋是为了规格化漫反射光,为后期的BRDF积分做准备。

+
+

此处的伦勃朗漫反射跟以前用的漫反射之间的关系:以前的漫反射是用表面的漫反射颜色乘以法线与入射光方向的点积,这个点积依然存在,只不过是被移到了BRDF外面,写作𝑛⋅𝜔i,放在反射方程𝐿𝑜靠后的位置。

+
+

BRDF的高光(镜面反射)部分更复杂:
+
+Cook-Torrance镜面反射BRDF由3个函数(𝐷,𝐹,𝐺)和一个标准化因子构成。𝐷,𝐹,𝐺符号各自近似模拟了特定部分的表面反射属性:

+
    +
  • 𝐷(Normal Distribution Function,NDF):法线分布函数,估算在受到表面粗糙度的影响下,取向方向与中间向量一致的微平面的数量。这是用来估算微平面的主要函数。
  • +
  • 𝐹(Fresnel equation):菲涅尔方程,描述的是在不同的表面角下表面反射的光线所占的比率。
  • +
  • 𝐺(Geometry function):几何函数,描述了微平面自成阴影的属性。当一个平面相对比较粗糙的时候,平面表面上的微平面有可能挡住其他的微平面从而减少表面所反射的光线。
  • +
+

以上的每一种函数都是用来估算相应的物理参数的,而且你会发现用来实现相应物理机制的每种函数都有不止一种形式。它们有的非常真实,有的则性能高效。你可以按照自己的需求任意选择自己想要的函数的实现方法。

+
+

Epic Games公司的Brian Karis对于这些函数的多种近似实现方式进行了大量的研究。这里将采用Epic Games在Unreal Engine 4中所使用的函数,其中𝐷使用Trowbridge-Reitz GGX,𝐹使用Fresnel-Schlick近似法(Approximation),而𝐺使用Smith's Schlick-GGX。

+
+

𝐷(Normal Distribution Function,NDF)

+

法线分布函数,从统计学上近似的表示了与某些(如中间)向量ℎ(半角向量)取向一致的微平面的比率。

+

目前有很多种NDF都可以从统计学上来估算微平面的总体取向度,只要给定一些粗糙度的参数以及一个我们马上将会要用到的参数Trowbridge-Reitz GGX(GGXTR):
+

+

这里的ℎ是用来测量微平面的半角向量,𝛼是表面的粗糙度,𝑛是表面法线。 如果将ℎ放到表面法线和光线方向之间,并使用不同的粗糙度作为参数,可以得到下面的效果:
+
+当粗糙度很低(表面很光滑)时,与中间向量ℎ取向一致的微平面会高度集中在一个很小的半径范围内。由于这种集中性,NDF最终会生成一个非常明亮的斑点。但是当表面比较粗糙的时候,微平面的取向方向会更加的随机,与向量ℎ取向一致的微平面分布在一个大得多的半径范围内,但是较低的集中性也会让最终效果显得更加灰暗。

+

Trowbridge-Reitz GGX的NDF实现代码:

+
float DistributionGGX(vec3 N, vec3 H, float a)
+{
+    float a2     = a*a;
+    float NdotH  = max(dot(N, H), 0.0);
+    float NdotH2 = NdotH*NdotH;
+
+    float nom    = a2;
+    float denom  = (NdotH2 * (a2 - 1.0) + 1.0);
+    denom        = PI * denom * denom;
+
+    return nom / denom;
+}
+
+
+

𝐹 (Fresnel equation)

+

菲涅尔方程定义的是在不同观察方向上,表面上被反射的光除以被折射的光的比例。在一束光击中了表面的一瞬间,菲涅尔根据表面与观察方向之间的夹角,计算得到光被反射的百分比。根据这个比例和能量守恒定律我们可以直接知道剩余的能量就是会被折射的能量。

+

当我们垂直观察每个表面或者材质时都有一个基础反射率,当我们以任意一个角度观察表面时所有的反射现象都会变得更明显(反射率高于基础反射率)。你可以从你身边的任意一件物体上观察到这个现象,当你以90度角观察你的桌子你会法线反射现象将会变得更加的明显,理论上以完美的90度观察任意材质的表面都应该会出现全反射现象(所有物体、材质都有菲涅尔现象)。

+

菲涅尔方程同样是个复杂的方程,但是幸运的是菲涅尔方程可以使用Fresnel-Schlick来近似:
+
+𝐹0表示的是表面基础反射率,这个我们可以使用一种叫做Indices of refraction(IOR)的方法计算得到。运用在球面上的效果就是你看到的那样,观察方向越是接近掠射角(grazing angle,又叫切线角,与正视角相差90度),菲涅尔现象导致的反射就越强:
+

+

菲涅尔方程中有几个微妙的地方,一个是Fresnel-Schlick算法仅仅是为电介质(绝缘体)表面定义的算法。对于金属表面,使用电介质的折射率来计算基础反射率是不合适的,我们需要用别的菲涅尔方程来计算。对于这个问题,我们需要预先计算表面在正视角(即以0度角正视表面)下的反应(𝐹0),然后就可以跟之前的Fresnel-Schlick算法一样,根据观察角度来进行插值。这样我们就可以用一个方程同时计算金属和电介质了。

+

表面在正视角下的反映或者说基础反射率可以在这个数据库中找到,下面是Naty Hoffman的在SIGGRAPH公开课中列举的一些常见材质的值:
+
+这里可以观察到的一个有趣的现象,所有电介质材质表面的基础反射率都不会高于0.17,这其实是例外而非普遍情况。导体材质表面的基础反射率起点更高一些并且(大多)在0.5和1.0之间变化。此外,对于导体或者金属表面而言基础反射率一般是带有色彩的,这也是为什么要用RGB三原色来表示的原因(法向入射的反射率可随波长不同而不同)。这种现象我们只能在金属表面观察的到。

+
+

金属表面这些和电介质表面相比所独有的特性引出了所谓的金属工作流的概念。也就是我们需要额外使用一个被称为金属度(Metalness)的参数来参与编写表面材质。金属度用来描述一个材质表面是金属还是非金属的。

+
+

通过预先计算电介质与导体的值,我们可以对两种类型的表面使用相同的Fresnel-Schlick近似,但是如果是金属表面的话就需要对基础反射率添加色彩。我们一般是按下面这个样子来实现的:

+
vec3 F0 = vec3(0.04);
+F0      = mix(F0, surfaceColor.rgb, metalness);
+
+

我们为大多数电介质表面定义了一个近似的基础反射率。𝐹0取最常见的电解质表面的平均值,这又是一个近似值。不过对于大多数电介质表面而言使用0.04作为基础反射率已经足够好了,而且可以在不需要输入额外表面参数的情况下得到物理可信的结果。然后,基于金属表面特性,我们要么使用电介质的基础反射率要么就使用𝐹0作来为表面颜色。因为金属表面会吸收所有折射光线而没有漫反射,所以我们可以直接使用表面颜色纹理来作为它们的基础反射率。

+

Fresnel Schlick近似可以用GLSL代码实现:

+
vec3 fresnelSchlick(float cosTheta, vec3 F0)
+{
+    return F0 + (1.0 - F0) * pow(1.0 - dot(n, v), 5.0);
+}
+
+
+

𝐺 (Geometry function)

+

几何函数模拟微平面相互遮挡导致光线的能量减少或丢失的现象。
+

+

类似NDF,几何函数也使用粗糙度作为输入参数,更粗糙意味着微平面产生自阴影的概率更高。几何函数使用由GGX和Schlick-Beckmann组合而成的模拟函数Schlick-GGX:
+
+这里的𝑘是使用粗糙度𝛼计算而来的,用于直接光照和IBL光照的几何函数的参数:
+
+需要注意的是这里𝛼的值取决于你的引擎怎么将粗糙度转化成𝛼,在接下来的教程中我们将会进一步讨论如何和在什么地方进行这个转换。

+

为了有效地模拟几何体,我们需要同时考虑两个视角,视线方向(几何遮挡)跟光线方向(几何阴影),我们可以用Smith函数将两部分放到一起:
+
+其中𝑣表示视线向量,𝐺𝑠𝑢𝑏(𝑛,𝑣,𝑘)表示视线方向的几何遮挡;𝑙表示光线向量,𝐺𝑠𝑢𝑏(𝑛,𝑙,𝑘)表示光线方向的几何阴影。使用Smith函数与Schlick-GGX作为𝐺𝑠𝑢𝑏可以得到如下所示不同粗糙度R的视觉效果:
+
+几何函数是一个值域为[0.0, 1.0]的乘数,其中白色(1.0)表示没有微平面阴影,而黑色(0.0)则表示微平面彻底被遮蔽。

+

使用GLSL编写的几何函数代码如下:

+
float GeometrySchlickGGX(float NdotV, float k)
+{
+    float nom   = NdotV;
+    float denom = NdotV * (1.0 - k) + k;
+
+    return nom / denom;
+}
+
+float GeometrySmith(vec3 N, vec3 V, vec3 L, float k)
+{
+    float NdotV = max(dot(N, V), 0.0);
+    float NdotL = max(dot(N, L), 0.0);
+    float ggx1 = GeometrySchlickGGX(NdotV, k); // 视线方向的几何遮挡
+    float ggx2 = GeometrySchlickGGX(NdotL, k); // 光线方向的几何阴影
+
+    return ggx1 * ggx2;
+}
+
+

Cook-Torrance反射方程(Cook-Torrance reflectance equation)

+

Cook-Torrance反射方程中的每一个部分我们我们都用基于物理的BRDF替换,可以得到最终的反射方程:
+
+上面的方程并非完全数学意义上的正确。前面提到菲涅尔项𝐹代表光在表面的反射比率,它直接影响𝑘𝑠因子,意味着反射方程的镜面反射部分已经隐含了因子𝑘𝑠。因此,最终的Cook-Torrance反射方程如下(去掉了𝑘𝑠):
+

+

这个方程完整地定义了一个基于物理的渲染模型,也就是我们一般所说的基于物理的渲染(PBR)。

+

PBR参数

+
    +
  • +

    反射率(Albedo):反射率纹理指定了材质表面每个像素的颜色,如果材质是金属那纹理包含的就是基础反射率。这个跟我们之前用过的漫反射纹理非常的类似,但是不包含任何光照信息。漫反射纹理通常会有轻微的阴影和较暗的裂缝,这些在Albedo贴图里面都不应该出现,仅仅只包含材质的颜色(金属材质是基础反射率)。

    +
  • +
  • +

    法线(Normal):法线纹理跟我们之前使用的是完全一样的。法线贴图可以逐像素指定表面法线,让平坦的表面也能渲染出凹凸不平的视觉效果。

    +
  • +
  • +

    金属度(Metallic):金属度贴图逐像素的指定表面是金属还是电介质。根据PBR引擎各自的设定,金属程度即可以是[0.0,1.0]区间的浮点值也可以是非0即1的布尔值。

    +
  • +
  • +

    粗糙度(Roughness):粗糙度贴图逐像素的指定了表面有多粗糙,粗糙度的值影响了材质表面的微平面的平均朝向,粗糙的表面上反射效果更大更模糊,光滑的表面更亮更清晰。有些PBR引擎用光滑度贴图替代粗糙度贴图,因为他们觉得光滑度贴图更直观,将采样出来的光滑度使用(1-光滑度)= 粗糙度 就能转换成粗糙度了。

    +
  • +
  • +

    环境光遮挡(Ambient Occlusion,AO):AO贴图为材质表面和几何体周边可能的位置,提供了额外的阴影效果。比如有一面砖墙,在两块砖之间的缝隙里Albedo贴图包含的应该是没有阴影的颜色信息,而让AO贴图来指定这一块需要更暗一些,这个地方光线更难照射到。AO贴图在光照计算的最后一步使用可以显著的提高渲染效果,模型或者材质的AO贴图一般是在建模阶段手动生成的。

    +
  • +
+

PBR的光照实现

+

上面阐述了Cook-Torrance反射方程的理论和公式意义。这节将探讨如何将前面讲到的理论转化成一个基于直接光照的渲染器:比如点光源,方向光和聚光灯。

+

辐照度计算

+

上面解释了Cook-Torrance反射方程的大部分含义,但有一点未提及:具体要怎么处理场景中的辐照度(Irradiance,也就是辐射的总能量𝐿)?在计算机领域,场景的辐射率𝐿度量的是来自光源光线的辐射通量𝜙穿过指定的立体角𝜔,在这里我们假设立体角𝜔无限小,小到辐射度衡量的是光源射出的一束经过指定方向向量的光线的通量。

+

有了这个假设,我们又要怎么将之融合到之前教程讲的光照计算里去呢?想象我们有一个辐射通量以RGB表示为(23.47, 21.31, 20.79)的点光源,这个光源的辐射强度等于辐射通量除以所有出射方向。当为平面上某个特定的点𝑝着色的时候,所有可能的入射光方向都会经过半球Ω,但只有一个入射方向𝜔i是直接来自点光源的,又因为我们的场景中只包含有一个光源,且这个光源只是一个点,所以𝑝点所有其它的入射光方向的辐射率都应该是0.
+
+如果我们暂时不考虑点光源的距离衰减问题,且无论光源放在什么地方入射光线的辐射率都一样大(忽略入射光角度cos𝜃对辐射度的影响),又因为点光源朝各个方向的辐射强度都是一样的,那么有效的辐射强度就跟辐射通量完全一样:恒定值(23.47, 21.31, 20.79)。

+

然而,辐射率需要使用位置𝑝作为输入参数,因为现实中的灯光根据点𝑝和光源之间距离的不同,辐射强度多少都会有一定的衰减。另外,从原始的辐射方程中我们可以发现,面法线𝑛于入射光方向向量𝜔i的点积也会影响结果。

+

用更精炼的话来描述:在点光源直接光照的情况里,辐射率函数𝐿计算的是灯光颜色,经过到𝑝点距离的衰减之后,再经过𝑛⋅𝜔i缩放。能击中点𝑝的光线方向𝜔i就是从𝑝点看向光源的方向。把这些写成代码:

+
vec3  lightColor  = vec3(23.47, 21.31, 20.79);
+vec3  wi          = normalize(lightPos - fragPos);
+float cosTheta    = max(dot(N, Wi), 0.0);
+// 计算光源在点fragPos的衰减系数
+float attenuation = calculateAttenuation(fragPos, lightPos); 
+// 英文原版的radiance类型有误,将它改成了vec3
+vec3 radiance  = lightColor * (attenuation * cosTheta);
+
+

你应该非常非常熟悉这段代码:这就是以前我们计算漫反射光的算法!在只有单光源直接光照的情况下,辐射率的计算方法跟我们以前的光照算法是类似的。

+
+

要注意我们这里假设点光源无限小,只是空间中的一个点。如果我们使用有体积的光源模型,那么就有很多的入射光方向的辐射率是非0的。 +对那些基于点的其他类型光源我们可以用类似的方法计算辐射率,比如平行光源的入射角的恒定的且没有衰减因子,聚光灯没有一个固定的辐射强度,而是围绕一个正前方向量来进行缩放的。

+
+

这也将我们带回了在表面半球Ω的积分∫。我们知道,多个单一位置的光源对同一个表面的同一个点进行光照着色并不需要用到积分,我们可以直接拿出这些数目已知的光源来,分别计算这些光源的辐照度后再加到一起,毕竟每个光源只有一束方向光能影响物体表面的辐射率。这样只需要通过相对简单的循环计算每个光源的贡献就能完成整个PBR光照计算。当我们需要使用IBL将环境光加入计算的时候我们才会需要用到积分,因为环境光可能来自任何方向。

+

PBR直接光照(Direct lighting)

+
+

示例代码LearnOpenGL的网站:lighting

+
+

基于图像的光照(Image Based Lighting, IBL)

+
+

基于图像的光照(IBL)是对光源物体的技巧集合,与直接光照不同,它将周围环境当成一个大光源。IBL通常结合cubemap环境贴图,cubemap通常采集自真实的照片或从3D场景生成,这样可以将其用于光照方程:cubemap的每个像素当成一个光源。这样可以更有效地捕获全局光照和常规感观,使得被渲染的物体更好地融入所处的环境中。

+
+

当基于图像的光照算法获得一些(全局的)环境光照时,它的输入被当成更加精密形式的环境光照,甚至是一种粗糙的全局光照的模拟。这使得IBL有助于PBR的渲染,使得物体渲染效果更真实。

+

在介绍IBL结合PBR之前,先回顾一下反射方程:
+

+

如之前所述,我们的主目标是解决所有入射光𝑤𝑖通过半球Ω的积分∫。与直接光照不同的是,在IBL中,每一个来自周围环境的入射光𝜔𝑖都可能存在辐射,这些辐射对解决积分有着重要的作用。为解决积分有两个要求:

+
    +
  • 需要用某种方法获得给定任意方向向量𝜔i的场景辐射。
  • +
  • 解决积分需尽可能快并实时。
  • +
+

对第一个要求,相对简单,采用环境cubemap。给定一个cubemap,可以假设它的每个像素是一个单独的发光光源。通过任意方向向量𝜔𝑖采样cubemap,可以获得场景在这个方向的辐射。

+

获取任意方向向量𝜔𝑖的场景辐射很简单,如下: +vec3 radiance = texture(_cubemapEnvironment, w_i).rgb; +对要求二,解决积分能只考虑一个方向的辐射,要考虑环境贴图的半球Ω的所有可能的方向𝜔i,但常规积分方法在片元着色器中开销非常大。为了有效解决积分问题,可采用预计算或预处理的方法。因此,需要深究一下反射方程:
+

+

可将上述的𝑘𝑑和𝑘𝑠项拆分:
+
+拆分后,可分开处理漫反射和镜面反射的积分。先从漫反射积分开始。

+

间接光的漫反射IBL(Indirect Diffuse IBL)

+

仔细分析上面方程的漫反射积分部分,发现Lambert漫反射是个常量项(颜色𝑐,折射因子𝑘𝑑和𝜋)并且不依赖积分变量。因此,可见常量部分移出漫反射积分:

+

+

因此,积分只依赖𝜔i(假设𝑝在环境贴图的中心)。据此,可以计算或预计算出一个新的cubemap,这个cubemap存储了用卷积(convolution)计算出的每个采样方向(或像素)𝜔𝑜的漫反射积分结果。

+

卷积(convolution)是对数据集的每个入口应用一些计算,假设其它所有的入口都在这个数据集里。此处的数据集就是场景辐射或环境图。因此,对cubemap的每个采样方向,我们可以顾及在半球Ω的其它所有的采样方向。

+

为了卷积环境图,我们要解决每个输出𝜔𝑜采样方向的积分,通过离散地采样大量的在半球Ω的方向𝜔i并取它们辐射的平均值。采样方向𝜔i的半球是以点𝑝为中心以𝜔𝑜为法平面的。

+

+

这个预计算的为每个采样方向𝜔𝑜存储了积分结果的cubemap,可被当成是预计算的在场景中所有的击中平行于𝜔𝑜表面的非直接漫反射的光照之和。这种cubemap被称为 辐照度图(Irradiance map)。

+
+

辐射方程依赖于位置𝑝,假设它在辐照度图的中心。这意味着所有非直接漫反射光需来自于同一个环境图,它可能打破真实的幻觉(特别是室内)。渲染引擎用放置遍布场景的反射探头(reflection probe)来解决,每个反射探头计算其所处环境的独自的辐照度图。这样,点p的辐射率(和辐射)是与其最近的反射探头的辐照度插值。这里我们假设总是在环境图的中心采样。反射探头将在其它章节探讨。

+
+

下面是cubemap环境图(下图左)和对应的辐照度图(下图右):
+

+

通过存储每个cubemap像素卷积的结果,辐照度图有点像环境的平均颜色或光照显示。从这个环境图采样任意方向,可获得这个方向的场景辐照度。

+
+

辐射度图提供了漫反射部分的积分,该积分表示来自非直接的所有方向的环境光辐射之和。由于辐射度图被当成是无方向性的光源,所以可以将漫反射镜面反射合成环境光。

+
+

在之前所述的反射方程中,非直接光依旧包含了漫反射和镜面反射两个部分,所以我们需要加个权重给漫反射。依旧使用菲涅尔方程来计算漫反射因子:

+
vec3 kS = fresnelSchlick(max(dot(N, V), 0.0), F0);
+vec3 kD = 1.0 - kS;
+vec3 irradiance = texture(ambientCubeMap, N).rgb;
+vec3 diffuse    = irradiance * albedo;
+vec3 ambient    = (kD * diffuse) * ao; 
+
+

由于环境光来自在半球内所有围绕着法线N的方向,没有单一的半向量去决定菲涅尔因子。为了仍然能模拟菲涅尔,这里采用了法线和视线的夹角。之前的算法采用了受表面粗糙度影响的微平面半向量,作为菲涅尔方程的输入。这里,我们并不考虑粗糙度,表面的反射因子被视作相当大。

+

非直接光照将沿用直接光照的相同的属性,所以,期望越粗糙的表面镜面反射越少。由于不考虑表面粗糙度,非直接光照的菲涅尔方程强度被视作粗糙的非金属表面(下图)。
+

+

为了缓解这个问题,可在Fresnel-Schlick方程注入粗糙度项(该方程的来源):

+
vec3 fresnelSchlickRoughness(float cosTheta, vec3 F0, float roughness)
+{
+    return F0 + (max(vec3(1.0 - roughness), F0) - F0) * pow(1.0 - cosTheta, 5.0);
+}   
+
+

考虑了表面粗糙度后,菲涅尔相关计算最终如下:

+
vec3 kS = fresnelSchlickRoughness(max(dot(N, V), 0.0), F0, roughness); 
+vec3 kD = 1.0 - kS;
+vec3 irradiance = texture(ambientCubeMap, N).rgb;
+vec3 diffuse    = irradiance * albedo;
+vec3 ambient    = (kD * diffuse) * ao; 
+
+

如上所述,实际上,基于图片的光照计算非常简单,只需要单一的cubemap纹理采样。大多数的工作在于预计算或卷积环境图到辐射度图。

+

加入了IBL的渲染效果如下(竖向是金属度增加,水平是粗糙度增加):

+

+

间接光的镜面反射IBL(Indirect Specular IBL)

+

上面描述的是IBL的漫反射部分,本节将讨论IBL的镜面反射部分先回顾一下反射方程:
+
+上述的镜面反射部分(被𝑘𝑠相乘)不是恒定的,并且依赖于入射光方向和视线入射方向,尝试实时地计算所有入射光和所有入射视线的积分是几乎不可能的。Epic Games推荐折中地使用预卷积镜面反射部分的方法来解决实时渲染的性能问题,这就是分裂和近似法(split sum approximation)。

+

分裂和近似法将镜面反射部分从反射方程分离出两个部分,这样可以单独地对它们卷积,后面在PBR的shader中为镜面的非直接IBL将它们结合起来。跟预卷积辐射度图类似,分裂和近似法需要HDR环境图作为输入。为了更好地理解分裂和近似法,下面着重关注反射方程的镜面部分:

+


+出于跟辐射度图相同的性能问题的考虑,我们要预计算类似镜面IBL图的积分,并且用片元的法线采样这个图。辐射度图的预计算只依赖于𝜔i,并且我们可以将漫反射项移出积分。但这次从BRDF可以看出,不仅仅是依赖于𝜔i
+

+

如上方程所示,还依赖𝜔𝑜,并且我们不能用两个方向向量来采样预计算的cubemap。预计算所有𝜔i和𝜔𝑜的组合在实时渲染环境中不实际的。

+

Epic Games的分裂和近似法将镜面反射部分从反射方程分离出两个部分,这样可以单独地对它们卷积,后面在PBR的shader中为镜面的非直接IBL将它们结合起来。分离后的方程如下:
+

+

第一部分预过滤环境图(pre-filtered environment map),类似于辐射度图的预计算环境卷积图,但会加入粗糙度。随着粗糙度等级的增加,环境图使用更多的散射采样向量来卷积,创建出更模糊的反射。

+

对每个卷积的粗糙度等级,循环地在预过滤环境图的mimap等级存储更加模糊的结果。下图是5个不同粗糙度等级的预过滤环境图:
+

+

生成采样向量和它们的散射强度,需要用到Cook-Torrance BRDF的法线分布图(NDF),而其带了两个输入:法线和视线向量。当卷积环境图时并不知道视线向量,Epic Games用了更近一步的模拟法:假设视线向量(亦即镜面反射向量)总是等于输出采样向量𝜔𝑜。所以代码变成如下所示:

+
vec3 N = normalize(w_o);
+vec3 R = N;
+vec3 V = R;
+
+
+

这种方式预过滤环境图卷积不需要关心视线方向。这就意味着当从某个角度看向下面这张图的镜面表面反射时,无法获得很好的掠射镜面反射(grazing specular reflections)。然而通常这被认为是一个较好的妥协:

+


+第二部分 镜面积分。假设所有方向的入射辐射率是全白的(那样𝐿(𝑝,𝑥)=1.0),那就可以用给定的粗糙度和一个法线𝑛和光源方向𝜔i之间的角度或𝑛⋅𝜔i来预计算BRDF的值。Epic Games存储了用变化的粗糙度来预计算每一个法线和光源方向组合的BRDF的值,该粗糙度存储于2D采样纹理(LUT)中,它被称为BRDF积分图(BRDF integration map)

+

2D采样纹理输出一个缩放(红色)和一个偏移值(绿色)给表面的菲涅尔方程式(Fresnel response),以便提供第二部分的镜面积分:
+

+

上图水平表示BRDF的输入𝑛⋅𝜔i,竖向表示输入的粗糙度。

+

有了预过滤环境图和BRDF积分图,可以在shader中将它们结合起来:

+
float lod             = getMipLevelFromRoughness(roughness);
+vec3 prefilteredColor = textureCubeLod(PrefilteredEnvMap, refVec, lod);
+vec2 envBRDF          = texture2D(BRDFIntegrationMap, vec2(NdotV, roughness)).xy;
+vec3 indirectSpecular = prefilteredColor * (F * envBRDF.x + envBRDF.y) 
+
+

间接光完整的IBL

+
+

示例代码:LearnOpenGL:IBL

+
+

PBR的两种工作流程

+
+

基础参数说明: +- Albedo + 可以理解为物体的基础色Base Color,即该物体本身颜色,也是漫反射光的颜色。

+

其实该颜是色光的折射被物质吸收并形成漫反射后,成为不同波长的光,这就赋予了物体颜色。比如说物体呈现蓝色,是因为其把除了蓝色的光都吸收了,散射出来的光的波长是在蓝色所在范围内。

+
    +
  • Metallic + 即金属度,表示了反射时发生镜面反射和漫反射的光线的占比。
  • +
+

Metallic度越大,发生镜面反射的占比越大,漫反射diffuse占比越小,一般金属物体的金属度比较大:70% ~ 100%之间;

+

Metallic越小,发生镜面反射的占比越小,漫反射diffuse占比越大,一般非金属物质金属度比较小;2% ~ 5%之间,宝石的大概8%;

+

金属是没有漫反射的,折射进表面的光照全部被吸收。但是一些腐蚀性的金属,其腐蚀的部分具有漫反射。

+
    +
  • Specular + 这个没什么好说的,就是镜面反射,对于非金属物质,其镜面反射的sRBG范围在40 ~ 75;对于金属物质,其sRGB范围在155 ~ 255
  • +
+

其也是表示0度时的菲涅尔系数RF(0°)。

+
    +
  • Roughness + 即粗糙度,其与Smoothness(光滑度,也称为Glossiness光泽度)相反。其表示了物体表面的不规则程度,决定了在发生镜面反射时入射光线与法线的夹角大小。
  • +
+

粗糙度roughness越大,镜面反射的出射光线分散的角度就越大,光照越模糊;

+

粗糙度roughness越小,镜面反射的出射光线分散的角度就越小,光照越尖锐;

+

在真实的生活中,视觉效果的呈现,主要取决于:

+
    +
  • 自然光照下,物体呈现的颜色(BasedColor/Albedo);
  • +
  • 物体表面对光线的镜面反射角度(Roughness);
  • +
  • 物体表面对光线镜面反射和漫反射的比例(Metallic/Specular);
  • +
+
+

Metal-Roughness(金属工作流)

+
+

UE4使用该工作流

+
+

在Metal-Rougnness流程中,分别对应BaseColor,Roughness,Metallic这三个参数;

+

在Metal-Roughness流程中,只要按照流程,分别设置好BaseColor,Roughness,Metallic,就可以基本确定物体材质的视觉效果;

+

Specular-Glossiness(高光工作流)

+
+

Unity使用该工作流

+
+

在Specular-Glossiness流程中,参数发生了变化,分别为Diffuse,Glossiness,Specular三个参数;

+

在Specular-Glossiness流程中,Diffuse和Specular共同决定了物体的basecolor,和表面镜面反射和漫反射的比例,与第一种流程的区别在于,此流程直接指定确定的占比值,第一种是根据Metallic属性,自动匹配相应的占比值;

+

两个流程的区别

+

共同点

+

都需要使用AO、Normal、Height纹理。

+

不同点

+
    +
  • +

    Metal/Roughness工作流程使用:Base Color、Roughness、Metallic三种纹理来作为基础纹理(最常用的工作流)。

    +
  • +
  • +

    Specular/Glossiness工作流程使用:Diffuse Map、Glossiness、Specular三种纹理来作为基础纹理。

    +
  • +
+
+

参考: +PBR原理
+由浅入深学习PBR的原理和实现
+LearnOpenGL:PBR理论
+LearnOpenGL:PBR Lighting

+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git "a/ANote/ParticleEffectForUGUI\344\275\277\347\224\250\346\225\231\347\250\213/index.html" "b/ANote/ParticleEffectForUGUI\344\275\277\347\224\250\346\225\231\347\250\213/index.html" new file mode 100644 index 00000000..71474c4a --- /dev/null +++ "b/ANote/ParticleEffectForUGUI\344\275\277\347\224\250\346\225\231\347\250\213/index.html" @@ -0,0 +1,8418 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ParticleEffectForUGUI使用教程 - 王国庆的博客 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + 跳转至 + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+ +
+ + + + + + + + + +

ParticleEffectForUGUI使用教程

+

UIParticle使用教程

+
+

Unity中 UGUI 使用 Particle System每次需要设置rendererorder和layer等信息来控制排序,非常繁琐,这里使用ParticleEffectForUGUI插件来解决该问题。
+ParticleEffectForUGUI

+
+

使用

+

设置Game窗口的分辨率 1920*1080

+

将带Particle System组件的物体放到UGUI根节点里面

+

将带Particle System组件的物体添加 UIParticle 组件,该组件会自动添加到子节点的所有带Particle System组件的物体上

+

+

Scale:调整该Particle System的缩放,不影响子物体
+Animatable Properties:如果使用Animation或者其他动画更新(Update)材质球上的属性,则勾选上对应的Shader属性即可
+

+

删除则需要每个物体去删除 UIParticle 组件

+

说明

+

每个带Particle System组件的物体都要增加 UIParticle 组件 (可以挂到prefab根节点物体上,可以是空物体)

+

ParticleEffectForUGUI会自动合批处理,不被打算的情况下

+

ParticleEffectForUGUI插件支持 Mask Rect Mask2D等遮罩,需要在使用的Shader上添加模板测试(Stencil)和ClipRect代码进行支持

+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git "a/ANote/SRPBatcher\345\222\214GPUInstance/index.html" "b/ANote/SRPBatcher\345\222\214GPUInstance/index.html" new file mode 100644 index 00000000..abe7b6b2 --- /dev/null +++ "b/ANote/SRPBatcher\345\222\214GPUInstance/index.html" @@ -0,0 +1,8578 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + SRPBatcher和GPUInstance - 王国庆的博客 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + 跳转至 + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ +
+ + + + + + + + + +

SRPBatcher和GPUInstance

+

GPU Instancing(unity内置渲染管线)

+
+

GPU Instancing 是对相同mesh的多份实例进行处理,例如树和草等
+instanceID:语义 SV_InstanceID,类型 uint,实例对应的数组索引。当启用实例化时,我们现在可以访问顶点程序中的实例 ID。有了它,我们可以在变换顶点位置时使用正确的矩阵。但是,UnityObjectToClipPos没有矩阵参数。它总是使用unity_ObjectToWorld. 为了解决这个问题,UnityInstancing包含unity_ObjectToWorld使用矩阵数组的宏覆盖文件。这可以被认为是一个肮脏的宏黑客,但它无需更改现有着色器代码即可工作,从而确保向后兼容性。(在使用实例化渲染调用时,gl_InstanceID会从0开始,在每个实例被渲染时递增1。比如说,我们正在渲染第43个实例,那么顶点着色器中它的gl_InstanceID将会是42。因为每个实例都有唯一的ID,我们可以建立一个数组,将ID与位置值对应起来,将每个实例放置在世界的不同位置。)
+教程:(Jasper Flick) GPU Instancing
+Jasper Flick详细说明了原理及 lod_fade对GPU Instancing的支持

+
+

简要概括:cpu将相同mesh的物体相且相同材质球每次次最大化数量提交到GPU,额外增加untiy_ObjectToWorld[]数组,及不同的颜色数组(如果有),顶点属性(instanceID)等等信息,减少了SetPassCall,实际GPU的DrawCall没有减少

+

条件: +- 相同mesh的物体 +- 相同材质球 (可以使用共享材质球)

+

支持使用MaterialPropertyBlock修改

+

SRP Batcher(SRP 自定义渲染管线)

+
+

教程:(Jasper Flick)SRP Batcher
+这里教程说明了SRP Batcher在2.1节,GPU Instancing(SRP渲染管线) 在2.3节

+
+

将属性写在 UnityPerMaterial 和 UnityPerDraw 中

+

条件: +- 相同Shader(允许不同材质球,不同mesh) +- 相同Shader的Keywords(变体) +- 对象不可以是粒子或蒙皮网格 +- 位置不相邻且中间夹杂不同的Shader或不同的变体的其他物体,不会同批处理(可以调整Queue来避免该情况)

+

不支持使用 ~~MaterialPropertyBlock~~ 修改,使用 MaterialPropertyBlock 对属性进行更改之后,不再合并,然后转到GPUIn stancing,如果支持的话。

+

SRP Batcher & GPU instancing的不同点和相同点

+
+

优先级顺序: SRP Batcher > Static Batching > GPU Instancing > Dynamic Batching

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
-Static BatchingDynamic BatchingGPU InstancingSRP Batcher
优点限制少自动无需处理性能极好多材质加速
缺点增加包体大小
增加运行时内存
增加运行时CPU消耗
限制多
限制多只能用于SRP中
适用场景静态场景
不适合大量重复物体
小物体、特效等大量重复物体较为广泛
+
+

参考:https://www.jianshu.com/p/f2a7e9ed9b89
+(Unity Doc) GPU Instancing +(Unity Doc) SRP Batcher

+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git "a/ANote/SSH\347\254\224\350\256\260/index.html" "b/ANote/SSH\347\254\224\350\256\260/index.html" new file mode 100644 index 00000000..913ed512 --- /dev/null +++ "b/ANote/SSH\347\254\224\350\256\260/index.html" @@ -0,0 +1,8908 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + SSH笔记 - 王国庆的博客 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + 跳转至 + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ +
+ + + + + + + + + +

SSH笔记

+

ssh-keygen 基本用法

+

ssh-keygen命令用于为“ssh”生成、管理和转换认证密钥,它支持RSA和DSA两种认证密钥.

+

ssh-keygen(选项)

+
-b:指定密钥长度; 
+-e:读取openssh的私钥或者公钥文件; 
+-C:添加注释; 
+-f:指定用来保存密钥的文件名; 
+-i:读取未加密的ssh-v2兼容的私钥/公钥文件,然后在标准输出设备上显示openssh兼容的私钥/公钥; 
+-l:显示公钥文件的指纹数据; 
+-N:提供一个新密语; 
+-P:提供(旧)密语;
+-q:静默模式; 
+-t:指定要创建的密钥类型。
+
+
+

特别注意 -C 参数: +-C:添加注释
+如果在github中配置ssh 则这个参数的值必须是github的邮件名称,不能是其他的

+
+

案例:

+
ssh-keygen -t rsa -f id_rsa_github -C "codingriver@163.com"
+# -t 指定要创建的密钥类型
+# -f 指定创建的密钥文件名字
+# -C 注释 注意如果是github配置ssh,这里必须是github的邮箱名称
+
+ssh-keygen -t ed25519 -C "codingriver@163.com"
+
+

验证ssh

+
# 验证github ssh配置是否成功
+ssh -vT git@github.com
+# or
+# 验证github ssh配置是否成功
+ssh -vT git@github.com
+
+

配置ssh密钥登录

+
    +
  • 在本地电脑上生成密钥 + ssh-keygen -t rsa + 生成密钥和公钥,将公钥复制到远程服务器的.ssh目录下
  • +
  • 远程ssh配置 + c + cd ~/.ssh + ls //ls你可以看到这个文件 + touch authorized_keys //生成认证文件,其实已经有了 + cat id_rsa.pub >> authorized_keys // 将上面生成公钥复制到.ssh后然后复制到authorized_keys文件 + chmod 600 authorized_keys //设置文件权限 + chmod 700 -R .ssh //是指文件夹权限
  • +
  • 远程sshd_config文件配置 + 文件位置: /etc/ssh/sshd_config,需要使用管理员权限打开 + 几步处理: + c + //使用密钥登录 + RSAAuthentication yes + PubkeyAuthentication yes + //禁止空密码和Root密码登录: + PermitEmptyPasswords no + PasswordAuthentication no
  • +
  • ssh登录测试
  • +
+
ssh -i .ssh/remote_rsa root@101.43.160.247
+// - i 指定密钥的文件路径
+// root 登录的用户名
+// 最后是ip
+
+
+

参考 https://blog.csdn.net/ouzuosong/article/details/52225087
+安装ssh https://www.cnblogs.com/wangboyu/articles/11611925.html

+
+

ssh登录

+
    +
  • 第一种 在命令行中指定私钥文件
  • +
+
# -i 指定私钥地址(私钥和公钥的文件名是一样的,只不过公钥文件有一个 .pub 后缀名。换句话说,如果把本地公钥给删了,只剩下私钥是无法登录的,因为在登录时要将公钥id发送给服务端,这样服务端才知道要选择哪个公钥加密)
+$ ssh -p 22 root@192.168.56.102 -i ~/.ssh/id_rsa_server
+
+
    +
  • 第二种 使用 ssh-agent 代理
  • +
+
# 先添加私钥
+$ ssh-add ~/.ssh/id_rsa_server
+# 查看添加的私钥
+$ ssh-add -l
+# 使用 ssh-agent 代理,ssh-agent 会在 ssh-add 列表中寻找到合适的私钥
+$ ssh root@192.168.56.102
+
+
    +
  • 第三种 在 SSH 配置中指定私钥文件
  • +
+
$ vim ~/.ssh/config
+
+Host gateway # 主机别名,使用 ssh gateway 命令可以直接登录该主机
+Protocol 2  # SSH 协议版本
+HostName example.com # 主机地址,支持IP或域名
+Port 22  # SSH 服务端口号
+User ubuntu # 登录用户名,会被 ssh root@gateway 覆盖,除非使用 ssh gateway
+IdentityFile ~/.ssh/id_rsa # 使用的私钥文件
+
+

例子:~/.ssh/config +权限:

+
# 为.ssh目录设置权限
+chmod 600 ~/.ssh/config
+
+
Host server
+  HostName 101.43.160.247
+  IdentityFile ~/.ssh/remote_rsa
+  User root
+  Port 22
+  ServerAliveInterval 60
+  ServerAliveCountMax 14400
+
+Host remote
+  HostName wgqing.com
+  IdentityFile ~/.ssh/remote_rsa
+  User root
+  Port 22
+  ServerAliveInterval 60
+  ServerAliveCountMax 14400  
+
+
+
+

参考:
+https://zhuanlan.zhihu.com/p/257430478
+https://blog.csdn.net/baalhuo/article/details/78067621

+
+

问题

+

关于ECDSA key fingerprint is

+
mrwang@CodingdeMBP .ssh % ssh -T git@github.com
+The authenticity of host 'github.com (20.205.243.166)' can't be established.
+ECDSA key fingerprint is SHA256:p2QAMXNIC1TJYWeIOttrVc98/R1BUFWu3/LiyKgUfQM.
+Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
+Warning: Permanently added 'github.com,20.205.243.166' (ECDSA) to the list of known hosts.
+git@github.com: Permission denied (publickey).
+mrwang@CodingdeMBP .ssh % ssh-add ~/.ssh/github
+Identity added: /Users/mrwang/.ssh/github (my mac for github codingriver@163.com)
+mrwang@CodingdeMBP .ssh % ssh -T git@github.com
+Hi codingriver! You've successfully authenticated, but GitHub does not provide shell access.
+
+

新建ssh,将公钥添加至github后,使用ssh -T git@github.com验证报ECDSA key fingerprint is xxx 错误。 +是因为新建SSH后没有开启代理

+

为SSH key 启用SSH代理

+
$ ssh-add ~/.ssh/id_rsa
+
+

ssh 出现Permission denied (publickey)问题

+
% git push
+git@github.com: Permission denied (publickey).
+fatal: Could not read from remote repository.
+
+Please make sure you have the correct access rights
+and the repository exists.
+
+

修改配置文件 /etc/ssh/ssh_config (windows系统 C:\Program Files\Git\etc\ssh\ssh_config),添加如下一行

+
   IdentityFile ~/.ssh/github_rsa  # github_rsa是密钥文件的名字
+
+

就可以了

+
+

这里可能是因为我修改密钥文件名字造成的,通配符匹配不到

+
+

+

SSH用私钥登录远程服务器时提示私钥不安全

+
+

使用 chmod 600 id_ed25519就好了,私钥文件权限问题

+
+
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+@         WARNING: UNPROTECTED PRIVATE KEY FILE!          @
+@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+Permissions 0644 for '/Users/mrwang/.ssh/***' are too open.
+It is required that your private key files are NOT accessible by others.
+This private key will be ignored.
+Load key "/Users/mrwang/.ssh/***": bad permissions
+git@codeup.aliyun.com: Permission denied (publickey).
+fatal: Could not read from remote repository.
+
+Please make sure you have the correct access rights
+and the repository exists.
+
+
+

ssh认证代理连接失败

+
+

需要执行下ssh-agent bash启动代理

+
+
$ ssh -vT git@github.com
+OpenSSH_9.1p1, OpenSSL 1.1.1s  1 Nov 2022
+debug1: Reading configuration data /etc/ssh/ssh_config
+debug1: Connecting to github.com [::1] port 22.
+debug1: Connection established.
+debug1: identity file /c/Users/coding/.ssh/id_rsa type -1
+debug1: identity file /c/Users/coding/.ssh/id_rsa-cert type -1
+debug1: identity file /c/Users/coding/.ssh/id_ecdsa type -1
+debug1: identity file /c/Users/coding/.ssh/id_ecdsa-cert type -1
+debug1: identity file /c/Users/coding/.ssh/id_ecdsa_sk type -1
+debug1: identity file /c/Users/coding/.ssh/id_ecdsa_sk-cert type -1
+debug1: identity file /c/Users/coding/.ssh/id_ed25519 type -1
+debug1: identity file /c/Users/coding/.ssh/id_ed25519-cert type -1
+debug1: identity file /c/Users/coding/.ssh/id_ed25519_sk type -1
+debug1: identity file /c/Users/coding/.ssh/id_ed25519_sk-cert type -1
+debug1: identity file /c/Users/coding/.ssh/id_xmss type -1
+debug1: identity file /c/Users/coding/.ssh/id_xmss-cert type -1
+debug1: identity file /c/Users/coding/.ssh/id_dsa type -1
+debug1: identity file /c/Users/coding/.ssh/id_dsa-cert type -1
+debug1: Local version string SSH-2.0-OpenSSH_9.1
+kex_exchange_identification: Connection closed by remote host
+Connection closed by ::1 port 22
+
+
+$ ssh-add id_rsa_github
+Could not open a connection to your authentication agent.
+
+
+$ ssh-add -l
+Could not open a connection to your authentication agent.
+
+
+

私钥的权限

+
-rw-r--r--@  1 mrwang  staff   432  3 11 13:18 id_ed25519
+-rw-r--r--@  1 mrwang  staff   113  3 11 13:18 id_ed25519.pub
+-rw-------   1 mrwang  staff  2602 12 19 22:37 id_rsa
+-rw-r--r--@  1 mrwang  staff   573 12 19 22:37 id_rsa.pub
+
+

使用 chmod 600 id_ed25519就好了,私钥文件权限问题

+
-rw-r--r--@ 1 mrwang  staff   627  2 12 18:52 config
+-rw-------@ 1 mrwang  staff   432  3 11 13:18 id_ed25519
+-rw-r--r--@ 1 mrwang  staff   113  3 11 13:18 id_ed25519.pub
+-rw-------  1 mrwang  staff  2602 12 19 22:37 id_rsa
+-rw-r--r--@ 1 mrwang  staff   573 12 19 22:37 id_rsa.pub
+
+
+

参考:https://my.oschina.net/philosopher/blog/314134

+
+

github使用ssh及小乌龟tortoisegit配置ssh的正确姿势

+

如果你也在使用小乌龟客户端,你可能会遇到使用SSH协议的仓库不能成功push的问题,这时你需要确保tortoisegit设置的SSH客户端是Git提供的才对。

+

+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git "a/ANote/Shader\346\241\210\344\276\213/index.html" "b/ANote/Shader\346\241\210\344\276\213/index.html" new file mode 100644 index 00000000..f004b73e --- /dev/null +++ "b/ANote/Shader\346\241\210\344\276\213/index.html" @@ -0,0 +1,9667 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Shader案例 - 王国庆的博客 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + 跳转至 + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+ +
+ + + + + + + + + +

Shader案例

+

角色展示

+

+
+

需要的贴图:
+漫反射贴图;法线贴图;Metal,Roughness,SkinArea通道贴图CompMask(M,R,S);次表面散射LUT查找图;IBL Specular CubeMap环境图;IBL Diffuse 球谐数据(提前读取IBL Diffuse CubeMap数据);头发Aniso各向异性Noise贴图

+
+


+漫反射,镜面反射,环境反射,环境高光

+

身体部分

+

组成: +- 直接光漫反射 + Lambert +- 直接光的镜面反射 + Blinn-Phong + 皮肤区域给一个很弱的高光(油光) +- 间接光的漫反射 + SH(球谐) +- 间接光的镜面反射 + CubeMap (Specular) +- LUT SSS次表面散射(皮肤处理) + LUT实现 +- 色彩矫正 + ToneMaping ACES

+
Shader "Custom/Role_Standard"
+{
+    Properties
+    {
+        _MainTex ("Texture", 2D) = "white" {}
+        _CompMask("CompMask(R M)",2D) = "white"{}
+        _NormalMap("NormalMap",2D) = "bump"{}
+        _SpecShininess("Spec Shininess",Float)=10
+        _RoughnessAdjust("Roughness Adjust",Range(-1,1))=0
+        _MetalAdjust("Metal Adjust",Range(-1,1))=0
+        _SkinLUT("Skin LUT",2D)="white"{}
+        _CurveOffset("Curve Offset",Range(0,1))=1
+        _LutOffset("LutOffset",Range(0,1))=0
+        [Header(IBL Specular)]
+        _Tint("Tint",Color)=(1,1,1,1)
+        _Expose("Expose",Float)=1.0     
+        _EnvMap("Env Cube",Cube)="white"{}
+        _Rotate("Rotate",Range(0,360))=0
+
+        [Toggle(_DIFFUSE_ON)]_DiffuseCheck("Diffuse Check",Float)=0
+        [Toggle(_SPECULAR_ON)]_Specular_Check("Specular Check",Float)=0
+        [Toggle(_SH_ON)]_SHCheck("SH Check",Float)=0
+        [Toggle(_IBL_ON)]_IBLCheck("IBL Check",Float)=0
+        [Toggle(_OILSKIN_ON)]_OilSkinCheck("Oil Skin Check",Float)=0
+
+        [Space(20)]
+        [Header(SH)]
+        [HideInInspector]custom_SHAr("Custom SHAr", Vector) = (0, 0, 0, 0)
+        [HideInInspector]custom_SHAg("Custom SHAg", Vector) = (0, 0, 0, 0)
+        [HideInInspector]custom_SHAb("Custom SHAb", Vector) = (0, 0, 0, 0)
+        [HideInInspector]custom_SHBr("Custom SHBr", Vector) = (0, 0, 0, 0)
+        [HideInInspector]custom_SHBg("Custom SHBg", Vector) = (0, 0, 0, 0)
+        [HideInInspector]custom_SHBb("Custom SHBb", Vector) = (0, 0, 0, 0)
+        [HideInInspector]custom_SHC("Custom SHC", Vector) = (0, 0, 0, 1)            
+    }
+    SubShader
+    {
+        Tags { "RenderType"="Opaque" }
+        LOD 100
+
+        Pass
+        {
+            Tags{"LightMode" = "ForwardBase"}
+            CGPROGRAM
+            #pragma vertex vert
+            #pragma fragment frag
+            #pragma multi_compile_fwdbase
+            #pragma shader_feature _DIFFUSE_ON
+            #pragma shader_feature _SPECULAR_ON
+            #pragma shader_feature _SH_ON
+            #pragma shader_feature _IBL_ON
+            #pragma shader_feature _OILSKIN_ON
+
+            #include "UnityCG.cginc"
+            #include"AutoLight.cginc"
+
+            struct appdata  // appdata_full
+            {
+                float4 vertex : POSITION; //模型空间顶点坐标
+                half2 texcoord : TEXCOORD0; //第一套UV(模型最多只能有4套UV)
+                half3 normal : NORMAL; //顶点法线
+                half4 tangent : TANGENT; //顶点切线(模型导入Unity后自动计算得到)
+            };
+
+            struct v2f
+            {
+                float4 pos : SV_POSITION; //输出裁剪空间下的顶点坐标数据,给光栅化使用,必须要写的数据
+                float2 uv : TEXCOORD0; //自定义数据体
+                half3 normal_dir : TEXCOORD1;
+                half3 tangent_dir : TEXCOORD2;
+                half3 binormal_dir : TEXCOORD3;
+                half3 pos_world : TEXCOORD4;
+                //最多可以写16个:TEXCOORD0 ~ TEXCOORD15。
+                LIGHTING_COORDS(5,6)   ///SHADOW 第一步
+            };
+
+            sampler2D _MainTex;
+            sampler2D _CompMask;
+            float4 _MainTex_ST;
+            float4 _LightColor0;
+            float _Shininess;
+            float4 _AmbientColor;
+            float _SpecIntensity;
+            sampler2D _AOMap;
+            sampler2D _SpecMask;
+            sampler2D _NormalMap;
+            float _NormalIntensity;
+            sampler2D _ParallaxMap;
+            float _Parallax;
+            float _SpecShininess;
+            float _RoughnessAdjust;
+            float _MetalAdjust;
+            float4 _Tint;
+            float _Expose;            
+            float _Rotate;
+            samplerCUBE _EnvMap;
+            float4 _EnvMap_HDR;
+            sampler2D _SkinLUT;
+            float _CurveOffset;
+            float _LutOffset;
+            half4 custom_SHAr;
+            half4 custom_SHAg;
+            half4 custom_SHAb;
+            half4 custom_SHBr;
+            half4 custom_SHBg;
+            half4 custom_SHBb;
+            half4 custom_SHC;   
+
+
+            float3 ShadeSH(float3 normal_dir)
+            {
+                float4 normalForSH = float4(normal_dir, 1.0);
+                //SHEvalLinearL0L1
+                half3 x;
+                x.r = dot(custom_SHAr, normalForSH);
+                x.g = dot(custom_SHAg, normalForSH);
+                x.b = dot(custom_SHAb, normalForSH);
+
+                //SHEvalLinearL2
+                half3 x1, x2;
+                // 4 of the quadratic (L2) polynomials
+                half4 vB = normalForSH.xyzz * normalForSH.yzzx;
+                x1.r = dot(custom_SHBr, vB);
+                x1.g = dot(custom_SHBg, vB);
+                x1.b = dot(custom_SHBb, vB);
+
+                // Final (5th) quadratic (L2) polynomial
+                half vC = normalForSH.x*normalForSH.x - normalForSH.y*normalForSH.y;
+                x2 = custom_SHC.rgb * vC;
+
+                float3 sh = max(float3(0.0, 0.0, 0.0), (x + x1 + x2));
+                sh = pow(sh, 1.0 / 2.2);  
+                return sh;
+            }
+
+            float3 ACESFilm(float3 x)
+            {
+                float a = 2.51f;
+                float b = 0.03f;
+                float c = 2.43f;
+                float d = 0.59f;
+                float e = 0.14f;
+                return saturate((x*(a*x + b)) / (x*(c*x + d) + e));
+            };
+
+            v2f vert (appdata v)
+            {
+                v2f o;
+                o.pos = UnityObjectToClipPos(v.vertex);
+                o.uv=TRANSFORM_TEX(v.texcoord,_MainTex);
+                o.pos_world=mul(unity_ObjectToWorld,v.vertex).xyz;
+                o.normal_dir=normalize(mul(v.normal,(float3x3)unity_WorldToObject));
+                o.tangent_dir=normalize(mul((float3x3)unity_ObjectToWorld,v.tangent.xyz));
+                o.binormal_dir=normalize(cross(o.normal_dir, o.tangent_dir))*v.tangent.w;
+                TRANSFER_VERTEX_TO_FRAGMENT(o); ///SHADOW 第二步
+                return o;
+            }
+
+            half4 frag (v2f i) : SV_Target
+            {
+                // Base Color
+                half4 base_color=tex2D(_MainTex,i.uv);
+                base_color=pow(base_color,2.2);// gamma转Liner空间
+                half3 comp_mask=tex2D(_CompMask,i.uv);         
+                half skin_area=1.0-comp_mask.b;
+                half roughness=saturate(comp_mask.r+_RoughnessAdjust);
+                half metal=saturate(comp_mask.g+ _MetalAdjust);
+                half3 spec_color=lerp(0.04,base_color.rgb,metal);
+                base_color=base_color*(1-metal);
+
+                half3 pos_world=i.pos_world;
+                half3 normal_dir=normalize(i.normal_dir);
+                half3 tangent_dir=normalize(i.tangent_dir);
+                half3 binormal_dir=normalize(i.binormal_dir);
+                half3 view_dir=normalize(_WorldSpaceCameraPos.xyz-pos_world);
+                float3x3 TBN=float3x3(tangent_dir,binormal_dir,normal_dir);
+
+
+                // Noraml 法线
+                half4 normalmap=tex2D(_NormalMap,i.uv);
+                half3 normal_data=UnpackNormal(normalmap);
+                // normal_data.xy = normal_data.xy * _NormalIntensity;
+                normal_dir=normalize(mul(normal_data,TBN));
+                //normal_dir = normalize(tangent_dir * normal_data.x * _NormalIntensity + binormal_dir * normal_data.y * _NormalIntensity + normal_dir * normal_data.z);
+
+                // Light Info
+                half3 light_dir=normalize(_WorldSpaceLightPos0.xyz);
+                half4 light_color=_LightColor0.rgba;
+                half atten=LIGHT_ATTENUATION(i);
+
+
+
+
+
+                // Direct Diffuse 漫反射,Lambert
+                half NdotL=max(0.0,dot(normal_dir,light_dir));
+                half3 diffuse= base_color.rgb*NdotL*light_color.rgb*atten;
+                half2 uv_lut=half2(NdotL*atten+_LutOffset,_CurveOffset);
+                half3 lut_color_gamma=tex2D(_SkinLUT,uv_lut);
+                half3 lut_color=pow(lut_color_gamma,2.2);
+                half3 sss_diffuse=lut_color*base_color*_LightColor0.rgb;
+                #ifdef _DIFFUSE_ON
+                half3 direct_diffuse=lerp(diffuse,sss_diffuse,skin_area);
+                #else
+                half3 direct_diffuse=half3(0,0,0);
+                #endif
+
+                // Direct Specular 镜面反射(高光) Blinn-Phong
+                half smoothness=1.0-roughness;
+                half3 half_dir=normalize(view_dir+light_dir);
+                half NdotH=max(0.0,dot(normal_dir,half_dir));
+                half shininess=lerp(1,_SpecShininess,smoothness);
+                half spec=pow(NdotH,shininess);
+                #ifdef _OILSKIN_ON
+                half3 spec_skin_color=lerp(spec_color,0.1,skin_area); //皮肤油光
+                #else
+                half3 spec_skin_color=spec_color;
+                #endif
+
+                #ifdef _SPECULAR_ON
+                half3 direct_specular=spec*spec_skin_color*light_color.rgb*atten;
+                #else
+                half3 direct_specular=half3(0,0,0);
+                #endif
+
+                //Indirect Diffuse 间接光的漫反射
+                float half_lambert=dot(normal_dir,light_dir)*0.5+0.5;
+                #ifdef _SH_ON
+                half3 env_diffuse= ShadeSH(normal_dir)*base_color*half_lambert;
+                #else
+                half3 env_diffuse=half3(0,0,0);
+                #endif                
+
+                // Indirect Specular 间接光的镜面反射
+                half3 rViewDir=reflect(-view_dir,normal_dir);
+                float rad=_Rotate*UNITY_PI/180;
+                float2x2 Rotation=float2x2(cos(rad),sin(rad),-sin(rad),cos(rad));
+                rViewDir.xz=mul(Rotation,rViewDir.xz);
+
+                roughness=roughness*(1.7-0.7*roughness);
+                float mip_level=(roughness)*6.0;
+                half4 color_cube=texCUBElod(_EnvMap,float4(rViewDir,mip_level));
+                half3 env_specular=DecodeHDR(color_cube,_EnvMap_HDR)*_Expose*spec_color*half_lambert*(1-skin_area);
+                #ifdef _IBL_ON
+                #else
+                env_specular=half3(0,0,0);
+                #endif                
+
+
+                half3 final_color = (direct_diffuse + direct_specular + env_diffuse+env_specular);
+                half3 tone_color = ACESFilm(final_color);
+                tone_color = pow(tone_color, 1.0 / 2.2);                
+                return half4(tone_color,1.0);
+                // return half4(final_color,1.0);
+            }
+            ENDCG
+        }
+
+    }
+    Fallback "Diffuse" ///SHADOW 第四步
+}
+
+
+
+

头发部分

+
+

头发处理:Kajiya-Kay各向异性头发(Kajiya-Kay头发

+
+

组成: +- 直接光的漫反射 + Lambert +- 直接光的镜面反射,双层高光 + Kajiya-Kay各向异性头发(Kajiya-Kay头发) +- 间接光的镜面反射 + CubeMap (Specular)

+
Shader "Custom/Role_Hair"
+{
+    Properties
+    {
+        _MainTex ("Texture", 2D) = "white" {}
+        _BaseColor("Base Color",Color)=(1,1,1,1)
+        _NormalMap("NormalMap",2D) = "bump"{}
+        _RoughnessAdjust("Roughness Adjust",Range(0,1))=0
+
+        [Header(Specular)]
+        _AnisoMap("Aniso Map",2D)="gray"{}
+        _SpecColor1("Spec Color 1",Color)=(1,1,1,1)
+        _SpecShininess1("Spec Shininess 1",Range(0,1))=0.1
+        _SpecNoise1("Spec Noise 1",float)=1
+        _SpecOffset1("Spec Offset 1",float)=0
+        _SpecColor2("Spec Color 2",Color)=(1,1,1,1)
+        _SpecShininess2("Spec Shininess 2",Range(0,1))=0.1
+        _SpecNoise2("Spec Noise 2",float)=1
+        _SpecOffset2("Spec Offset 2",float)=0       
+
+        [Header(IBL Specular)]
+        _Tint("Tint",Color)=(1,1,1,1)
+        _Expose("Expose",Float)=1.0     
+        _EnvMap("Env Cube",Cube)="white"{}
+        _Rotate("Rotate",Range(0,360))=0
+
+        [Toggle(_DIFFUSE_ON)]_DiffuseCheck("Diffuse Check",Float)=0
+        [Toggle(_SPECULAR_ON)]_Specular_Check("Specular Check",Float)=0
+        // [Toggle(_SH_ON)]_SHCheck("SH Check",Float)=0
+        [Toggle(_IBL_ON)]_IBLCheck("IBL Check",Float)=0     
+    }
+    SubShader
+    {
+        Tags { "RenderType"="Opaque" }
+        LOD 100
+
+        Pass
+        {
+            Tags{"LightMode" = "ForwardBase"}
+            CGPROGRAM
+            #pragma vertex vert
+            #pragma fragment frag
+            #pragma multi_compile_fwdbase
+            #pragma shader_feature _DIFFUSE_ON
+            #pragma shader_feature _SPECULAR_ON
+            #pragma shader_feature _SH_ON
+            #pragma shader_feature _IBL_ON
+            #pragma shader_feature _OILSKIN_ON
+
+            #include "UnityCG.cginc"
+            #include"AutoLight.cginc"
+
+            struct appdata  // appdata_full
+            {
+                float4 vertex : POSITION; //模型空间顶点坐标
+                half2 texcoord : TEXCOORD0; //第一套UV(模型最多只能有4套UV)
+                half3 normal : NORMAL; //顶点法线
+                half4 tangent : TANGENT; //顶点切线(模型导入Unity后自动计算得到)
+            };
+
+            struct v2f
+            {
+                float4 pos : SV_POSITION; //输出裁剪空间下的顶点坐标数据,给光栅化使用,必须要写的数据
+                float2 uv : TEXCOORD0; //自定义数据体
+                half3 normal_dir : TEXCOORD1;
+                half3 tangent_dir : TEXCOORD2;
+                half3 binormal_dir : TEXCOORD3;
+                half3 pos_world : TEXCOORD4;
+                //最多可以写16个:TEXCOORD0 ~ TEXCOORD15。
+                LIGHTING_COORDS(5,6)   ///SHADOW 第一步
+            };
+
+            sampler2D _MainTex;
+            sampler2D _CompMask;
+            float4 _MainTex_ST;
+            float4 _LightColor0;
+            float _Shininess;
+            float4 _AmbientColor;
+            float _SpecIntensity;
+            sampler2D _AOMap;
+            sampler2D _SpecMask;
+            sampler2D _NormalMap;
+            float _NormalIntensity;
+            sampler2D _ParallaxMap;
+            float _Parallax;
+            float _SpecShininess;
+            float _RoughnessAdjust;
+            float _MetalAdjust;
+            float4 _Tint;
+            float _Expose;            
+            float _Rotate;
+            samplerCUBE _EnvMap;
+            float4 _EnvMap_HDR;
+            sampler2D _SkinLUT;
+            float _CurveOffset;
+            float _LutOffset;
+            half4 custom_SHAr;
+            half4 custom_SHAg;
+            half4 custom_SHAb;
+            half4 custom_SHBr;
+            half4 custom_SHBg;
+            half4 custom_SHBb;
+            half4 custom_SHC;   
+            float _BaseColor;
+            sampler2D _AnisoMap;
+            float4 _AnisoMap_ST;
+
+
+        float4 _SpecColor1;
+        float _SpecShininess1;
+        float _SpecNoise1;
+        float _SpecOffset1;
+        float4 _SpecColor2;
+        float _SpecShininess2;
+        float _SpecNoise2;
+        float _SpecOffset2;
+
+            float3 ACESFilm(float3 x)
+            {
+                float a = 2.51f;
+                float b = 0.03f;
+                float c = 2.43f;
+                float d = 0.59f;
+                float e = 0.14f;
+                return saturate((x*(a*x + b)) / (x*(c*x + d) + e));
+            };
+
+            v2f vert (appdata v)
+            {
+                v2f o;
+                o.pos = UnityObjectToClipPos(v.vertex);
+                o.uv=TRANSFORM_TEX(v.texcoord,_MainTex);
+                o.pos_world=mul(unity_ObjectToWorld,v.vertex).xyz;
+                o.normal_dir=normalize(mul(v.normal,(float3x3)unity_WorldToObject));
+                o.tangent_dir=normalize(mul((float3x3)unity_ObjectToWorld,v.tangent.xyz));
+                o.binormal_dir=normalize(cross(o.normal_dir, o.tangent_dir))*v.tangent.w;
+                TRANSFER_VERTEX_TO_FRAGMENT(o); ///SHADOW 第二步
+                return o;
+            }
+
+            half4 frag (v2f i) : SV_Target
+            {
+                // Base Color
+                half4 base_color=tex2D(_MainTex,i.uv);
+                base_color=pow(base_color,2.2);// gamma转Liner空间
+                half4 spec_color=base_color;
+
+                half roughness=saturate(_RoughnessAdjust);
+
+                half3 pos_world=i.pos_world;
+                half3 normal_dir=normalize(i.normal_dir);
+                half3 tangent_dir=normalize(i.tangent_dir);
+                half3 binormal_dir=normalize(i.binormal_dir);
+                half3 view_dir=normalize(_WorldSpaceCameraPos.xyz-pos_world);
+                float3x3 TBN=float3x3(tangent_dir,binormal_dir,normal_dir);
+
+
+                // Noraml 法线
+                half4 normalmap=tex2D(_NormalMap,i.uv);
+                half3 normal_data=UnpackNormal(normalmap);
+                // normal_data.xy = normal_data.xy * _NormalIntensity;
+                normal_dir=normalize(mul(normal_data,TBN));
+                //normal_dir = normalize(tangent_dir * normal_data.x * _NormalIntensity + binormal_dir * normal_data.y * _NormalIntensity + normal_dir * normal_data.z);
+
+                // Light Info
+                half3 light_dir=normalize(_WorldSpaceLightPos0.xyz);
+                half4 light_color=_LightColor0.rgba;
+                half atten=LIGHT_ATTENUATION(i);
+
+
+
+
+
+                // Direct Diffuse 漫反射,Lambert
+
+                half NdotL=max(0.0,dot(normal_dir,light_dir));
+                half half_lambert=NdotL*0.5+0.5;
+                half3 direct_diffuse=base_color;
+
+                // Direct Specular 镜面反射(高光) Blinn-Phong
+                float aniso_noise=tex2D(_AnisoMap,i.uv*_AnisoMap_ST.xy+_AnisoMap_ST.zw).r-0.5;
+
+                half3 half_dir=normalize(view_dir+light_dir);
+                half NdotH=dot(normal_dir,half_dir);
+                half TdotH=dot(tangent_dir,half_dir);
+
+                half NdotV=max(0.0,dot(normal_dir,view_dir));
+                float aniso_atten=saturate(sqrt(max(0.0,half_lambert/NdotV)))*atten;
+
+                // spec1
+                float3 spec_color1=_SpecColor1.rgb+base_color;
+                half3 b_offset1=normal_dir*(_SpecOffset1+_SpecNoise1*aniso_noise);
+                half3 binormal_dir1=normalize(binormal_dir+b_offset1);
+                half BdotH1=dot(binormal_dir1,half_dir)/_SpecShininess1;
+                float3 spec_term1=exp(-(TdotH*TdotH+BdotH1*BdotH1)/(1.0+NdotH));
+                float3 final_spec1=spec_term1*aniso_atten*spec_color1*light_color.rgb;
+
+                //spec2 
+                float3 spec_color2=_SpecColor2.rgb+base_color;
+                half3 b_offset2=normal_dir*(_SpecOffset2+_SpecNoise2*aniso_noise);
+                half3 binormal_dir2=normalize(binormal_dir+b_offset2);
+                half BdotH2=dot(binormal_dir2,half_dir)/_SpecShininess2;
+                float3 spec_term2=exp(-(TdotH*TdotH+BdotH2*BdotH2)/(1.0+NdotH));
+                float3 final_spec2=spec_term2*aniso_atten*spec_color2*light_color.rgb;                
+
+                // Indirect Specular 间接光的镜面反射
+                half3 rViewDir=reflect(-view_dir,normal_dir);
+                float rad=_Rotate*UNITY_PI/180;
+                float2x2 Rotation=float2x2(cos(rad),sin(rad),-sin(rad),cos(rad));
+                rViewDir.xz=mul(Rotation,rViewDir.xz);
+
+                roughness=roughness*(1.7-0.7*roughness);
+                float mip_level=(roughness)*6.0;
+                half4 color_cube=texCUBElod(_EnvMap,float4(rViewDir,mip_level));
+                float3 color_ibl=DecodeHDR(color_cube,_EnvMap_HDR);
+                half3 env_specular=color_ibl*_Expose*half_lambert*aniso_noise;
+                #ifdef _IBL_ON
+                #else
+                env_specular=half3(0,0,0);
+                #endif                
+                #ifdef _DIFFUSE_ON
+                #else
+                direct_diffuse=half3(0,0,0);
+                #endif   
+                #ifdef _SPECULAR_ON
+                #else
+                final_spec1=half3(0,0,0);
+                final_spec2=half3(0,0,0);
+                #endif                                   
+
+                half3 final_color = (final_spec1 + final_spec2+direct_diffuse+env_specular);
+                // half3 final_color = (direct_diffuse + direct_specular+env_specular);
+                half3 tone_color = ACESFilm(final_color);
+                tone_color = pow(tone_color, 1.0 / 2.2);                
+                return half4(tone_color,1.0);
+                // return half4(final_color,1.0);
+            }
+            ENDCG
+        }
+
+    }
+    Fallback "Diffuse" ///SHADOW 第四步
+}
+
+
+

玉龙

+

+漫反射,透射光(高光),环境光

+

透射光实现:根据光反方向(增加法线的扭曲,表示玉龙表面的粗糙情况影响光线的方向)和视线方向夹角来判定透射光的强度(假设从玉龙后面有个手电筒和人眼的夹角,越小表示人眼看到的光线越多,光越强),然后通过厚度图来模拟玉龙的通透性

+
Shader "CS03/Dragon"
+{
+    Properties
+    {
+        _DiffuseColor ("Diffuse Color", Color) = (1,1,1,1)
+        _Opacity("Opacity",Float)=1
+        _AddColor ("Add Color", Color) = (1,1,1,1)
+        _Distort("Distort",Range(0,1))=0
+        _Power("Power",Float)=2
+        _Scale("Scale",Float)=1
+        _ThicknessMap ("Thickness Map", 2D) = "black" {}
+        _CubeMap ("Cube Map", CUBE) = "white" {}
+        _EnvRotate("Env Rotate",Range(0,360))=0
+    }
+    SubShader
+    {
+        Tags { "RenderType"="Opaque" }
+        LOD 100
+
+        Pass
+        {
+            Tags{"LightMode" = "ForwardBase"}
+            CGPROGRAM
+            #pragma vertex vert
+            #pragma fragment frag
+            #pragma multi_compile_fwdbase
+            #include "UnityCG.cginc"
+            #include"AutoLight.cginc"
+
+            struct appdata  // appdata_full
+            {
+                float4 vertex : POSITION; //模型空间顶点坐标
+                half2 texcoord : TEXCOORD0; //第一套UV(模型最多只能有4套UV)
+                half2 texcoord1 : TEXCOORD1; //第二套UV
+                half2 texcoord2 : TEXCOORD2; //第三套UV
+                half2 texcoord3 : TEXCOORD3;  //第四套UV,模型最多只能有4套UV
+                half4 color : COLOR; //顶点颜色
+                half3 normal : NORMAL; //顶点法线
+                half4 tangent : TANGENT; //顶点切线(模型导入Unity后自动计算得到)
+            };
+
+            struct v2f
+            {
+                float4 pos : SV_POSITION; //输出裁剪空间下的顶点坐标数据,给光栅化使用,必须要写的数据
+                float2 uv : TEXCOORD0; //自定义数据体
+                half3 normal_dir : TEXCOORD1;
+                half3 tangent_dir : TEXCOORD2;
+                half3 binormal_dir : TEXCOORD3;
+                half3 pos_world : TEXCOORD4;
+                //最多可以写16个:TEXCOORD0 ~ TEXCOORD15。
+            };
+
+            sampler2D _MainTex;
+            float4 _MainTex_ST;
+            float4 _LightColor0;
+            sampler2D _NormalMap;
+            float _Distort;
+            float _Power;
+            float _Scale;
+            sampler2D _ThicknessMap;
+            samplerCUBE _CubeMap;
+            float4 _CubeMap_HDR;
+            float _EnvRotate;
+            float4 _DiffuseColor;
+            float4 _AddColor;
+            float _Opacity;
+
+            v2f vert (appdata v)
+            {
+                v2f o;
+                o.pos = UnityObjectToClipPos(v.vertex);
+                o.uv=TRANSFORM_TEX(v.texcoord,_MainTex);
+                o.pos_world=mul(unity_ObjectToWorld,v.vertex).xyz;
+                o.normal_dir=normalize(mul(v.normal,(float3x3)unity_WorldToObject));
+                o.tangent_dir=normalize(mul((float3x3)unity_ObjectToWorld,v.tangent.xyz));
+                o.binormal_dir=normalize(cross(o.normal_dir, o.tangent_dir))*v.tangent.w;
+                return o;
+            }
+
+            half4 frag (v2f i) : SV_Target
+            {
+                half3 pos_world=i.pos_world;
+                half3 normal_dir=normalize(i.normal_dir);
+                half3 tangent_dir=normalize(i.tangent_dir);
+                half3 binormal_dir=normalize(i.binormal_dir);
+                half3 view_dir=normalize(_WorldSpaceCameraPos.xyz-pos_world);
+                float3x3 TBN=float3x3(tangent_dir,binormal_dir,normal_dir);
+                // Light
+                half3 light_dir=normalize(_WorldSpaceLightPos0.xyz);
+                half4 light_color=_LightColor0.rgba;
+                half atten=1.0;
+
+                //漫反射
+                float NdotL=max(0.0,dot(normal_dir,light_dir));
+                float3 color_diffuse=NdotL*_DiffuseColor.rgb*light_color.rgb;
+                float sky_light=(dot(normal_dir,float3(0,1,0))+1.0)*0.5;
+                float3 color_sky=sky_light*_DiffuseColor.rgb;
+                float3 final_diffuse=color_diffuse+_AddColor.rgb+color_sky*_Opacity;
+
+                // 透射光
+                float3 back_dir=-normalize(light_dir+normal_dir*_Distort);
+                float VdotB=max(0.0,dot(view_dir,back_dir));
+                float backlight_term=max(0.0,pow(VdotB,_Power))*_Scale;
+                float thickness=1.0-tex2D(_ThicknessMap,i.uv).r;
+                half3 back_color=backlight_term*light_color*thickness;           
+
+                // 光泽反射
+                float3 rView_dir=reflect(-view_dir,normal_dir);
+                float drag=_EnvRotate*UNITY_PI/180.0;
+                float2x2 Roration=float2x2(cos(drag),sin(drag),-sin(drag),cos(drag));
+                rView_dir.xz=mul(Roration,rView_dir.xz);
+                half4 color_cube=texCUBE(_CubeMap,rView_dir);
+                float3 color_env=DecodeHDR(color_cube,_CubeMap_HDR);
+                half fresnel=1.0-max(0.0,dot(normal_dir,view_dir));
+                float3 final_env=color_env*fresnel;
+
+                float3 color_final=final_diffuse+final_env+ back_color;
+
+                // return half4(thickness.xxx,1.0);
+                return half4(color_final,1.0);
+            }
+            ENDCG
+        }     
+        Pass
+        {
+            Tags{"LightMode" = "ForwardAdd"}
+            Blend One One
+            CGPROGRAM
+            #pragma vertex vert
+            #pragma fragment frag
+            #pragma multi_compile_fwdadd
+            #include "UnityCG.cginc"
+            #include"AutoLight.cginc"
+
+            struct appdata  // appdata_full
+            {
+                float4 vertex : POSITION; //模型空间顶点坐标
+                half2 texcoord : TEXCOORD0; //第一套UV(模型最多只能有4套UV)
+                half2 texcoord1 : TEXCOORD1; //第二套UV
+                half2 texcoord2 : TEXCOORD2; //第三套UV
+                half2 texcoord3 : TEXCOORD3;  //第四套UV,模型最多只能有4套UV
+                half4 color : COLOR; //顶点颜色
+                half3 normal : NORMAL; //顶点法线
+                half4 tangent : TANGENT; //顶点切线(模型导入Unity后自动计算得到)
+            };
+
+            struct v2f
+            {
+                float4 pos : SV_POSITION; //输出裁剪空间下的顶点坐标数据,给光栅化使用,必须要写的数据
+                float2 uv : TEXCOORD0; //自定义数据体
+                half3 normal_dir : TEXCOORD1;
+                half3 tangent_dir : TEXCOORD2;
+                half3 binormal_dir : TEXCOORD3;
+                half3 pos_world : TEXCOORD4;
+                //最多可以写16个:TEXCOORD0 ~ TEXCOORD15。
+                LIGHTING_COORDS(5,6)
+            };
+
+            sampler2D _MainTex;
+            float4 _MainTex_ST;
+            float4 _LightColor0;
+            sampler2D _NormalMap;
+            float _Distort;
+            float _Power;
+            float _Scale;
+            sampler2D _ThicknessMap;
+            samplerCUBE _CubeMap;
+            float4 _CubeMap_HDR;
+            float _EnvRotate;
+            float4 _DiffuseColor;
+            float4 _AddColor;
+            float _Opacity;
+
+            v2f vert (appdata v)
+            {
+                v2f o;
+                o.pos = UnityObjectToClipPos(v.vertex);
+                o.uv=TRANSFORM_TEX(v.texcoord,_MainTex);
+                o.pos_world=mul(unity_ObjectToWorld,v.vertex).xyz;
+                o.normal_dir=normalize(mul(v.normal,(float3x3)unity_WorldToObject));
+                o.tangent_dir=normalize(mul((float3x3)unity_ObjectToWorld,v.tangent.xyz));
+                o.binormal_dir=normalize(cross(o.normal_dir, o.tangent_dir))*v.tangent.w;
+                TRANSFER_VERTEX_TO_FRAGMENT(o);
+                return o;
+            }
+
+            half4 frag (v2f i) : SV_Target
+            {
+                half shadow = SHADOW_ATTENUATION(i); ///SHADOW 第三步
+
+                half3 pos_world=i.pos_world;
+                half3 normal_dir=normalize(i.normal_dir);
+                half3 tangent_dir=normalize(i.tangent_dir);
+                half3 binormal_dir=normalize(i.binormal_dir);
+                half3 view_dir=normalize(_WorldSpaceCameraPos.xyz-pos_world);
+                float3x3 TBN=float3x3(tangent_dir,binormal_dir,normal_dir);
+                // Light
+                half3 light_dir=normalize(_WorldSpaceLightPos0.xyz);
+                half4 light_color=_LightColor0.rgba;
+                half atten=LIGHT_ATTENUATION(i);
+
+                // 透射光
+                float3 back_dir=-normalize(light_dir+normal_dir*_Distort);
+                float VdotB=max(0.0,dot(view_dir,back_dir));
+                float backlight_term=max(0.0,pow(VdotB,_Power))*_Scale;
+                float thickness=1.0-tex2D(_ThicknessMap,i.uv).r;
+                half3 back_color=backlight_term*light_color*thickness;           
+
+                float3 color_final=back_color;
+                return half4(color_final,1.0);
+            }
+            ENDCG
+        }             
+    }
+    Fallback "Diffuse" ///SHADOW 第四步
+}
+
+
+
+

钻石

+

+

双Pass渲染,一个背面渲染(cubemap的反射和折射),一个前面渲染(折射和菲涅尔)

+

ASE

+
// Made with Amplify Shader Editor
+// Available at the Unity Asset Store - http://u3d.as/y3X 
+Shader "CS07/Diamond"
+{
+    Properties
+    {
+        _ColorA("Color A", Color) = (0.02491992,0.09269338,0.754717,0)
+        _RefractTex("RefractTex", CUBE) = "white" {}
+        _ReflectTex("ReflectTex", CUBE) = "white" {}
+        _ReflectIntensity("ReflectIntensity", Float) = 1
+        _RefractIntensity("RefractIntensity", Float) = 1
+        _RimPower("RimPower", Float) = 5
+        _RimBias("RimBias", Float) = 0
+        _RimScale("RimScale", Float) = 0
+        _RimColor("RimColor", Color) = (0,0,0,0)
+
+    }
+
+    SubShader
+    {
+
+
+        Tags { "RenderType"="Opaque" "Queue"="Geometry" }
+    LOD 100
+
+
+
+
+        Pass
+        {
+            Name "Unlit"
+            Tags { "LightMode"="ForwardBase" }
+
+        CGINCLUDE
+        #pragma target 3.0
+        ENDCG
+        Blend Off
+        AlphaToMask Off
+        Cull Front
+        ColorMask RGBA
+        ZWrite On
+        ZTest LEqual
+        Offset 0 , 0
+
+
+            CGPROGRAM
+
+
+
+            #ifndef UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX
+            //only defining to not throw compilation error over Unity 5.5
+            #define UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input)
+            #endif
+            #pragma vertex vert
+            #pragma fragment frag
+            #pragma multi_compile_instancing
+            #include "UnityCG.cginc"
+            #define ASE_NEEDS_FRAG_WORLD_POSITION
+
+
+            struct appdata
+            {
+                float4 vertex : POSITION;
+                float4 color : COLOR;
+                float3 ase_normal : NORMAL;
+                UNITY_VERTEX_INPUT_INSTANCE_ID
+            };
+
+            struct v2f
+            {
+                float4 vertex : SV_POSITION;
+                #ifdef ASE_NEEDS_FRAG_WORLD_POSITION
+                float3 worldPos : TEXCOORD0;
+                #endif
+                float4 ase_texcoord1 : TEXCOORD1;
+                UNITY_VERTEX_INPUT_INSTANCE_ID
+                UNITY_VERTEX_OUTPUT_STEREO
+            };
+
+            uniform float4 _ColorA;
+            uniform samplerCUBE _RefractTex;
+            uniform samplerCUBE _ReflectTex;
+            uniform float _RefractIntensity;
+
+
+            v2f vert ( appdata v )
+            {
+                v2f o;
+                UNITY_SETUP_INSTANCE_ID(v);
+                UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
+                UNITY_TRANSFER_INSTANCE_ID(v, o);
+
+                float3 ase_worldNormal = UnityObjectToWorldNormal(v.ase_normal);
+                o.ase_texcoord1.xyz = ase_worldNormal;
+
+
+                //setting value to unused interpolator channels and avoid initialization warnings
+                o.ase_texcoord1.w = 0;
+                float3 vertexValue = float3(0, 0, 0);
+                #if ASE_ABSOLUTE_VERTEX_POS
+                vertexValue = v.vertex.xyz;
+                #endif
+                vertexValue = vertexValue;
+                #if ASE_ABSOLUTE_VERTEX_POS
+                v.vertex.xyz = vertexValue;
+                #else
+                v.vertex.xyz += vertexValue;
+                #endif
+                o.vertex = UnityObjectToClipPos(v.vertex);
+
+                #ifdef ASE_NEEDS_FRAG_WORLD_POSITION
+                o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
+                #endif
+                return o;
+            }
+
+            fixed4 frag (v2f i ) : SV_Target
+            {
+                UNITY_SETUP_INSTANCE_ID(i);
+                UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(i);
+                fixed4 finalColor;
+                #ifdef ASE_NEEDS_FRAG_WORLD_POSITION
+                float3 WorldPosition = i.worldPos;
+                #endif
+                float3 ase_worldNormal = i.ase_texcoord1.xyz;
+                float3 ase_worldViewDir = UnityWorldSpaceViewDir(WorldPosition);
+                ase_worldViewDir = normalize(ase_worldViewDir);
+                float3 ase_worldReflection = reflect(-ase_worldViewDir, ase_worldNormal);
+                float4 texCUBENode7 = texCUBE( _ReflectTex, ase_worldReflection );
+                float4 temp_output_12_0 = ( _ColorA * texCUBE( _RefractTex, ase_worldReflection ) * texCUBENode7 * _RefractIntensity );
+
+
+                finalColor = temp_output_12_0;
+                return finalColor;
+            }
+            ENDCG
+        }
+
+        Pass
+        {
+            Name "Second"
+            Tags { "LightMode"="ForwardBase" }
+
+        CGINCLUDE
+        #pragma target 3.0
+        ENDCG
+        Blend One One
+        AlphaToMask Off
+        Cull Back
+        ColorMask RGBA
+        ZWrite On
+        ZTest LEqual
+        Offset 0 , 0
+
+
+            CGPROGRAM
+
+
+
+            #ifndef UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX
+            //only defining to not throw compilation error over Unity 5.5
+            #define UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input)
+            #endif
+            #pragma vertex vert
+            #pragma fragment frag
+            #pragma multi_compile_instancing
+            #include "UnityCG.cginc"
+            #define ASE_NEEDS_FRAG_WORLD_POSITION
+
+
+            struct appdata
+            {
+                float4 vertex : POSITION;
+                float4 color : COLOR;
+                float3 ase_normal : NORMAL;
+                UNITY_VERTEX_INPUT_INSTANCE_ID
+            };
+
+            struct v2f
+            {
+                float4 vertex : SV_POSITION;
+                #ifdef ASE_NEEDS_FRAG_WORLD_POSITION
+                float3 worldPos : TEXCOORD0;
+                #endif
+                float4 ase_texcoord1 : TEXCOORD1;
+                UNITY_VERTEX_INPUT_INSTANCE_ID
+                UNITY_VERTEX_OUTPUT_STEREO
+            };
+
+            uniform float4 _ColorA;
+            uniform samplerCUBE _RefractTex;
+            uniform samplerCUBE _ReflectTex;
+            uniform float _RefractIntensity;
+            uniform float _ReflectIntensity;
+            uniform float _RimPower;
+            uniform float _RimScale;
+            uniform float _RimBias;
+            uniform float4 _RimColor;
+
+
+            v2f vert ( appdata v )
+            {
+                v2f o;
+                UNITY_SETUP_INSTANCE_ID(v);
+                UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
+                UNITY_TRANSFER_INSTANCE_ID(v, o);
+
+                float3 ase_worldNormal = UnityObjectToWorldNormal(v.ase_normal);
+                o.ase_texcoord1.xyz = ase_worldNormal;
+
+
+                //setting value to unused interpolator channels and avoid initialization warnings
+                o.ase_texcoord1.w = 0;
+                float3 vertexValue = float3(0, 0, 0);
+                #if ASE_ABSOLUTE_VERTEX_POS
+                vertexValue = v.vertex.xyz;
+                #endif
+                vertexValue = vertexValue;
+                #if ASE_ABSOLUTE_VERTEX_POS
+                v.vertex.xyz = vertexValue;
+                #else
+                v.vertex.xyz += vertexValue;
+                #endif
+                o.vertex = UnityObjectToClipPos(v.vertex);
+
+                #ifdef ASE_NEEDS_FRAG_WORLD_POSITION
+                o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
+                #endif
+                return o;
+            }
+
+            fixed4 frag (v2f i ) : SV_Target
+            {
+                UNITY_SETUP_INSTANCE_ID(i);
+                UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(i);
+                fixed4 finalColor;
+                #ifdef ASE_NEEDS_FRAG_WORLD_POSITION
+                float3 WorldPosition = i.worldPos;
+                #endif
+                float3 ase_worldNormal = i.ase_texcoord1.xyz;
+                float3 ase_worldViewDir = UnityWorldSpaceViewDir(WorldPosition);
+                ase_worldViewDir = normalize(ase_worldViewDir);
+                float3 ase_worldReflection = reflect(-ase_worldViewDir, ase_worldNormal);
+                float4 texCUBENode7 = texCUBE( _ReflectTex, ase_worldReflection );
+                float4 temp_output_12_0 = ( _ColorA * texCUBE( _RefractTex, ase_worldReflection ) * texCUBENode7 * _RefractIntensity );
+                float dotResult21 = dot( ase_worldNormal , ase_worldViewDir );
+                float clampResult23 = clamp( dotResult21 , 0.0 , 1.0 );
+                float temp_output_24_0 = ( 1.0 - clampResult23 );
+                float4 temp_output_16_0 = ( temp_output_12_0 + ( texCUBENode7 * _ReflectIntensity * temp_output_24_0 ) );
+                float saferPower25 = max( temp_output_24_0 , 0.0001 );
+                float temp_output_30_0 = ( ( max( pow( saferPower25 , _RimPower ) , 0.0 ) * _RimScale ) + _RimBias );
+
+
+                finalColor = ( temp_output_16_0 + ( temp_output_16_0 * temp_output_30_0 * ( temp_output_30_0 * _RimColor ) ) );
+                return finalColor;
+            }
+            ENDCG
+        }
+
+    }
+    CustomEditor "ASEMaterialInspector"
+
+
+}
+/*ASEBEGIN
+Version=18500
+2500;488;1639;969;571.4478;525.8101;1.694416;True;False
+Node;AmplifyShaderEditor.CommentaryNode;40;-1788.975,781.5402;Inherit;False;2012.625;729.3613;Fresnel;13;22;19;21;23;26;24;25;27;29;28;31;30;32;;1,1,1,1;0;0
+Node;AmplifyShaderEditor.WorldNormalVector;19;-1738.975,831.5402;Inherit;False;False;1;0;FLOAT3;0,0,1;False;4;FLOAT3;0;FLOAT;1;FLOAT;2;FLOAT;3
+Node;AmplifyShaderEditor.ViewDirInputsCoordNode;22;-1716.198,1112.462;Inherit;False;World;False;0;4;FLOAT3;0;FLOAT;1;FLOAT;2;FLOAT;3
+Node;AmplifyShaderEditor.DotProductOpNode;21;-1330.5,1006.167;Inherit;False;2;0;FLOAT3;0,0,0;False;1;FLOAT3;0,0,0;False;1;FLOAT;0
+Node;AmplifyShaderEditor.ClampOpNode;23;-1181.688,992.5009;Inherit;False;3;0;FLOAT;0;False;1;FLOAT;0;False;2;FLOAT;1;False;1;FLOAT;0
+Node;AmplifyShaderEditor.OneMinusNode;24;-1000.987,1000.093;Inherit;False;1;0;FLOAT;0;False;1;FLOAT;0
+Node;AmplifyShaderEditor.RangedFloatNode;26;-1107.281,1180.793;Inherit;False;Property;_RimPower;RimPower;5;0;Create;True;0;0;False;0;False;5;10;0;0;0;1;FLOAT;0
+Node;AmplifyShaderEditor.PowerNode;25;-809.6565,994.0192;Inherit;False;True;2;0;FLOAT;0;False;1;FLOAT;1;False;1;FLOAT;0
+Node;AmplifyShaderEditor.WorldReflectionVector;13;-1153.126,32.63672;Inherit;False;False;1;0;FLOAT3;0,0,0;False;4;FLOAT3;0;FLOAT;1;FLOAT;2;FLOAT;3
+Node;AmplifyShaderEditor.RangedFloatNode;29;-647.2493,1140.901;Inherit;False;Property;_RimScale;RimScale;7;0;Create;True;0;0;False;0;False;0;5;0;0;0;1;FLOAT;0
+Node;AmplifyShaderEditor.SimpleMaxOpNode;27;-617.2493,995.9019;Inherit;False;2;0;FLOAT;0;False;1;FLOAT;0;False;1;FLOAT;0
+Node;AmplifyShaderEditor.SamplerNode;7;-562.063,172.0891;Inherit;True;Property;_ReflectTex;ReflectTex;2;0;Create;True;0;0;False;0;False;-1;None;7ecb52b18a453124283cee921dc388cf;True;0;False;white;LockedToCube;False;Object;-1;Auto;Cube;8;0;SAMPLERCUBE;;False;1;FLOAT3;0,0,0;False;2;FLOAT;0;False;3;FLOAT3;0,0,0;False;4;FLOAT3;0,0,0;False;5;FLOAT;1;False;6;FLOAT;0;False;7;SAMPLERSTATE;;False;5;COLOR;0;FLOAT;1;FLOAT;2;FLOAT;3;FLOAT;4
+Node;AmplifyShaderEditor.ColorNode;2;-506.5,-301.5;Inherit;False;Property;_ColorA;Color A;0;0;Create;True;0;0;False;0;False;0.02491992,0.09269338,0.754717,0;0.5754717,0.5754717,0.5754717,0;True;0;5;COLOR;0;FLOAT;1;FLOAT;2;FLOAT;3;FLOAT;4
+Node;AmplifyShaderEditor.RangedFloatNode;17;-514.6671,98.24844;Inherit;False;Property;_RefractIntensity;RefractIntensity;4;0;Create;True;0;0;False;0;False;1;5;0;0;0;1;FLOAT;0
+Node;AmplifyShaderEditor.SamplerNode;6;-570.063,-90.91089;Inherit;True;Property;_RefractTex;RefractTex;1;0;Create;True;0;0;False;0;False;-1;None;e9efb1d234a58c04cbb0a0c9eb87d214;True;0;False;white;LockedToCube;False;Object;-1;Auto;Cube;8;0;SAMPLERCUBE;;False;1;FLOAT3;0,0,0;False;2;FLOAT;0;False;3;FLOAT3;0,0,0;False;4;FLOAT3;0,0,0;False;5;FLOAT;1;False;6;FLOAT;0;False;7;SAMPLERSTATE;;False;5;COLOR;0;FLOAT;1;FLOAT;2;FLOAT;3;FLOAT;4
+Node;AmplifyShaderEditor.SimpleMultiplyOpNode;28;-399.2493,1052.902;Inherit;False;2;2;0;FLOAT;0;False;1;FLOAT;0;False;1;FLOAT;0
+Node;AmplifyShaderEditor.RangedFloatNode;31;-469.4467,1211.618;Inherit;False;Property;_RimBias;RimBias;6;0;Create;True;0;0;False;0;False;0;0;0;0;0;1;FLOAT;0
+Node;AmplifyShaderEditor.RangedFloatNode;15;-466.0444,392.8555;Inherit;False;Property;_ReflectIntensity;ReflectIntensity;3;0;Create;True;0;0;False;0;False;1;1;0;0;0;1;FLOAT;0
+Node;AmplifyShaderEditor.SimpleAddOpNode;30;-172.2493,1094.901;Inherit;False;2;2;0;FLOAT;0;False;1;FLOAT;0;False;1;FLOAT;0
+Node;AmplifyShaderEditor.ColorNode;36;255.3527,989.3035;Inherit;False;Property;_RimColor;RimColor;8;0;Create;True;0;0;False;0;False;0,0,0,0;0.8970588,0.8623443,0.4551255,0;True;0;5;COLOR;0;FLOAT;1;FLOAT;2;FLOAT;3;FLOAT;4
+Node;AmplifyShaderEditor.SimpleMultiplyOpNode;18;-131.6671,296.2484;Inherit;False;3;3;0;COLOR;0,0,0,0;False;1;FLOAT;0;False;2;FLOAT;0;False;1;COLOR;0
+Node;AmplifyShaderEditor.SimpleMultiplyOpNode;12;-81.06299,-120.9109;Inherit;False;4;4;0;COLOR;0,0,0,0;False;1;COLOR;0,0,0,0;False;2;COLOR;0,0,0,0;False;3;FLOAT;0;False;1;COLOR;0
+Node;AmplifyShaderEditor.SimpleAddOpNode;16;119.3329,185.2484;Inherit;False;2;2;0;COLOR;0,0,0,0;False;1;COLOR;0,0,0,0;False;1;COLOR;0
+Node;AmplifyShaderEditor.SimpleMultiplyOpNode;35;460.0375,793.4158;Inherit;False;2;2;0;FLOAT;0;False;1;COLOR;0,0,0,0;False;1;COLOR;0
+Node;AmplifyShaderEditor.SimpleMultiplyOpNode;33;309.5573,294.3183;Inherit;False;3;3;0;COLOR;0,0,0,0;False;1;FLOAT;0;False;2;COLOR;0,0,0,0;False;1;COLOR;0
+Node;AmplifyShaderEditor.SimpleAddOpNode;34;540.5623,209.1047;Inherit;False;2;2;0;COLOR;0,0,0,0;False;1;COLOR;0,0,0,0;False;1;COLOR;0
+Node;AmplifyShaderEditor.FresnelNode;32;-708.2494,1308.901;Inherit;False;Standard;WorldNormal;ViewDir;False;False;5;0;FLOAT3;0,0,1;False;4;FLOAT3;0,0,0;False;1;FLOAT;0;False;2;FLOAT;1;False;3;FLOAT;5;False;1;FLOAT;0
+Node;AmplifyShaderEditor.TemplateMultiPassMasterNode;39;977.4616,398.2424;Float;False;False;-1;2;ASEMaterialInspector;100;9;New Amplify Shader;0a6bbb37052a2458b860677ab2960714;True;Second;0;1;Second;2;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;True;2;RenderType=Opaque=RenderType;Queue=Geometry=Queue=0;False;0;True;4;1;False;-1;1;False;-1;0;1;False;-1;0;False;-1;True;0;False;-1;0;False;-1;False;False;False;False;False;False;True;0;False;-1;True;0;False;-1;True;True;True;True;True;0;False;-1;False;False;False;True;False;255;False;-1;255;False;-1;255;False;-1;7;False;-1;1;False;-1;1;False;-1;1;False;-1;7;False;-1;1;False;-1;1;False;-1;1;False;-1;True;0;False;-1;True;0;False;-1;True;True;0;False;-1;0;False;-1;True;1;LightMode=ForwardBase;True;2;0;;0;0;Standard;0;False;0
+Node;AmplifyShaderEditor.TemplateMultiPassMasterNode;38;987.6282,-86.66805;Float;False;True;-1;2;ASEMaterialInspector;100;9;CS07/Diamond;0a6bbb37052a2458b860677ab2960714;True;Unlit;0;0;Unlit;2;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;True;2;RenderType=Opaque=RenderType;Queue=Geometry=Queue=0;False;0;True;0;1;False;-1;0;False;-1;0;1;False;-1;0;False;-1;True;0;False;-1;0;False;-1;False;False;False;False;False;False;True;0;False;-1;True;1;False;-1;True;True;True;True;True;0;False;-1;False;False;False;True;False;255;False;-1;255;False;-1;255;False;-1;7;False;-1;1;False;-1;1;False;-1;1;False;-1;7;False;-1;1;False;-1;1;False;-1;1;False;-1;True;0;False;-1;True;0;False;-1;True;True;0;False;-1;0;False;-1;True;1;LightMode=ForwardBase;True;2;0;;0;0;Standard;1;Vertex Position,InvertActionOnDeselection;1;0;2;True;True;False;;False;0
+WireConnection;21;0;19;0
+WireConnection;21;1;22;0
+WireConnection;23;0;21;0
+WireConnection;24;0;23;0
+WireConnection;25;0;24;0
+WireConnection;25;1;26;0
+WireConnection;27;0;25;0
+WireConnection;7;1;13;0
+WireConnection;6;1;13;0
+WireConnection;28;0;27;0
+WireConnection;28;1;29;0
+WireConnection;30;0;28;0
+WireConnection;30;1;31;0
+WireConnection;18;0;7;0
+WireConnection;18;1;15;0
+WireConnection;18;2;24;0
+WireConnection;12;0;2;0
+WireConnection;12;1;6;0
+WireConnection;12;2;7;0
+WireConnection;12;3;17;0
+WireConnection;16;0;12;0
+WireConnection;16;1;18;0
+WireConnection;35;0;30;0
+WireConnection;35;1;36;0
+WireConnection;33;0;16;0
+WireConnection;33;1;30;0
+WireConnection;33;2;35;0
+WireConnection;34;0;16;0
+WireConnection;34;1;33;0
+WireConnection;39;0;34;0
+WireConnection;38;0;12;0
+ASEEND*/
+//CHKSM=D24C8BF7840C56CD057B1AAEC0FA1C82BDCD7B7D
+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git "a/ANote/Shader\347\254\224\350\256\260/index.html" "b/ANote/Shader\347\254\224\350\256\260/index.html" new file mode 100644 index 00000000..0ed71bd2 --- /dev/null +++ "b/ANote/Shader\347\254\224\350\256\260/index.html" @@ -0,0 +1,9917 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Shader笔记 - 王国庆的博客 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + 跳转至 + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+ +
+ + + + + + + + + +

Shader笔记

+
+

Shader Language目前主要有3种语言 +- 基于OpenGL的OpenGL Shading Language,简称 GLSL; +- 基于DirectX的High Level Shading Language,简称 HLSL; +- 还有NVIDIA公司的C for Graphic,简称 Cg 语言

+

渲染相关博客及网站 +catlikecoding :render相关博客

+
+

渲染管线流程

+
CPU应用阶段: 视锥体剔除,渲染顺序,提交Drawcall
+顶点处理   : 顶点MVP空间变换,自定义参数
+光栅化操作 : 裁剪,NDC归一化,背面剔除,屏幕坐标,图元装配,光栅化
+片元处理   : 光照着色,纹理着色
+输出合并   : Alpha测试,模版测试,深度测试,颜色混合
+最后输出到帧缓冲区
+
+

#### CPU应用程序渲染逻辑 +a. 剔除: +- 视锥体剔除(Frustum Culling) +- 层级剔除(Layer Culling Mask),遮挡剔除(Occlusion Culling)等规则

+

b. 渲染排序: +- 渲染队列 RenderQueue +- 不透明队列(RenderQueue < 2500) + 按摄像机 从前往后 排序 +- 半透明队列(RenderQueue >2500) + 按摄像机 从后往前 排序(为了保证效果的正确性)

+

c. 打包数据(Batch):大量数据,参数发送到gpu

+

模型信息 + - 顶点坐标 + - 法线 + - UV + - 切线 + - 顶点颜色 + - 索引列表

+

矩阵 + - 世界变换矩阵 + - VP矩阵:根据射线机位置和fov等参数构建VP矩阵 + - 当矩阵的w分量为0的时候Unity会将其视为向量,而当w分量为1的时候Unity将其视为位置

+

灯光,材质参数 + - Shader + - 材质参数 + - 灯光信息

+

d. 调用Shader + - SetPassCall(Shader,背面剔除等参数,设置渲染数据),DrawCall

+


+20210410181112
+
+半透明渲染顺序效果对比
+

+

GPU渲染管线

+
+

CPU端调用DrawCall后 在GPU端启动顶点shader执行顶点处理 +顶点Shader:最主要的处理是将模型空间的顶点变换到裁剪空间 +- 顶点处理 : 顶点MVP空间变换,自定义参数 +- 光栅化操作 : 裁剪,NDC归一化,背面剔除,屏幕坐标,图元装配,光栅化 +- 片元处理 : 光照着色,纹理着色 +- 输出合并 : Alpha测试,模版测试,深度测试,颜色混合
+
+20210410184548
+
+20210410190836
+
+20210410190724 +裁剪操作是在长方体或者正方体范围内进行的,不是视锥体,这里图中只是表达要进行三角形剔除
+

+
+
颜色混合
+

常用颜色混合类型 +- 正常(Alpha Blend),即透明度混合
+Blend SrcAlpha OneMinusSrcAlpha +- Particle Additive
+Blend SrcAlpha One +- 柔和叠加(Soft Additive)
+Blend OneMinusDstColor One +- 线性减淡(Additive,Linear Dodge)
+Blend One One ("LightMode" = "ForwardAdd" 光源叠加Pass使用的混合模式) +- 正片叠底(Multiply),即相乘
+Blend DstColor Zero +- 两倍相乘(2x Multiply)
+Blend DstColor SrcColor +- 变暗(Darken)
+BlendOp Min
+Blend One One
+- 变亮(Lighten)
+BlendOp Max
+Blend One One
+- 滤色(Screen)
+Blend OneMinusDstColor One
+Blend One OneMinusSrcColor

+

空间变换

+
    +
  1. 模型空间(M)(左手坐标系)
  2. +
  3. 世界空间(W)(左手坐标系)
  4. +
  5. 观察空间(V)(右手坐标系)
  6. +
  7. 裁剪空间(P)(左手坐标系)
  8. +
  9. 屏幕空间(左手坐标系)
  10. +
+
+

裁剪空间是正方形或者长方形,下一步ndc归一化是除以w就到正负1范围内,(z轴在opengl 范围是正负1,在dx中范围是从0到1) +NDC归一化后进行背面剔剔除(Back Face Culling)根据三角形的索引顺序进行判定背面(三角形索引顺序是顺时针)或者正面(三角形索引顺序是逆时针),然后剔除对应三角形

+
+

+

类型长度

+
    +
  • float:32位
  • +
  • half :16位 精度范围-60000~+60000
  • +
  • fixed:11位 精度范围-2.0~+2.0;也可能是8位
  • +
+

一般使用fixed存储颜色和单位矢量

+

Gamma校正(Gamma Correct)

+
+

参考:https://zhuanlan.zhihu.com/p/66558476

+
+

+

+

+

点线代表线性颜色/亮度值(译注:这表示的是理想状态,Gamma为1),实线代表监视器显示的颜色。

+

Gamma校正(Gamma Correction)的思路是在最终的颜色输出上应用监视器Gamma的倒数。回头看前面的Gamma曲线图,你会有一个短划线,它是监视器Gamma曲线的翻转曲线。我们在颜色显示到监视器的时候把每个颜色输出都加上这个翻转的Gamma曲线,这样应用了监视器Gamma以后最终的颜色将会变为线性的。我们所得到的中间色调就会更亮,所以虽然监视器使它们变暗,但是我们又将其平衡回来了。

+
    +
  • 显示器的输出在Gamma2.2空间。(显示器自动计算)
  • +
  • 伽马校正会将颜色转换到Gamma0.45空间。
  • +
  • 伽马校正和显示器输出平衡之后,结果就是Gamma1.0的线性空间。
  • +
+
//为了有效解决color的值域问题,我们可以使用色调映射(Tone Map)和曝光控制(Exposure Map),用它们将color的高动态范围(HDR)映射到LDR之后再做伽马校正:
+color = color / (color + vec3(1.0)); // 色调映射(Tone Map)
+color = pow(color, vec3(1.0/2.2));   // 伽马校正
+
+

sRGB纹理

+

基于gamma0.45的颜色空间叫做sRGB颜色空间(Gamma 空间)。

+

把sRGB纹理变回线性空间:

+
float gamma = 2.2;
+vec3 linear_diffuseColor = pow(texture(sRGB_diffuse, texCoords).rgb, vec3(gamma));
+
+

变回线性空间后可以进行光照计算等等一系列计算 +线性空间转到Gamma空间:

+
float gamma = 2.2;
+vec3 gamma_diffuseColor = pow(linear_color, vec3(1.0/gamma));
+
+
+

Unity中sRGB(Color Texture)选项说明:
+sRGB (Color Texture)        启用此属性可指定将纹理存储在伽马空间中。对于非 HDR 颜色纹理(例如反照率和镜面反射颜色),应始终选中此复选框。如果纹理存储了有特定含义的信息,并且您需要着色器中的确切值(例如,平滑度或金属度),请禁用此属性。默认情况下会启用此属性。

+
+

linear和srgb,HDR区别

+
+

linear模式

+

在PBR流程中,环境光线效果需要使用linear模式,原来的gamma模式不再适用。因为gamma模式本身是为了使其他技术渲染出来的模型具有更真实而效果,对环境光进行了修正。但是在pbr中,贴图在设计的时候,完全是按照真实的环境中的光照去计算的,所以此处不再需要修正。

+

HDR(High Dynamic Range)

+

也叫 高动态范围 。 +显示器被限制为只能显示值为0.0到1.0间的颜色(LDR),但是在光照方程中却没有这个限制。通过使片段的颜色超过1.0,我们有了一个更大的颜色范围,这也被称作HDR(High Dynamic Range, 高动态范围)。有了HDR,亮的东西可以变得非常亮,暗的东西可以变得非常暗,而且充满细节。

+
+

人眼有光线自适应的特性,这样也是能让人在暗的场景里看到更多东西,在亮的场景里能分辨更多东西,这种效果一般从电影院这样昏暗的场所里走出来更能体会,眼睛会有一个慢慢适应的过程。现在摄像头一般也会自适应曝光度,但是工业需求的有些还是需要不自动的,三维渲染之中,如果把这种真实完全模拟的图像给人眼看到,会感觉比我们人眼看到灰暗的多,所以一般会在硬件方面做一个反曲线最后变成srgb的色彩空间让人看到。

+

游戏引擎里面最终效果给人看到,当然是这种强化过的适应人眼的色彩空间,但是我做光照计算和贴图就不行,因为会由于多次的色彩强化导致最终画面强烈失真,这时候就是需要linear的色彩空间,计算时候用真实色彩,直到输出这一步把色彩强化一遍以适合人眼。unity很早就是linear的色彩空间,但是由于后期最后一部的矫正方面很多从业人士的素养不足,或者完全没有这个意识,做出来的效果完全是不正确,或者缺乏调整弹性的。而unreal则在工作流上面几乎是整合和后期的色表,去解决这个问题。

+

而HDR是模拟人眼的过程,能看到更广范围的光(就是亮的时候能更亮瞎),HDR图像的一般色域也超过普通的RGB色域(但也可以不超过)。HDR在unity中需要勾选摄像机上的HDR选项和使用延迟渲染deferred才能(否则会有滤镜提示The camera is not HDR enabled 这是没使用延迟渲染),要看出效果还要加个bloom之类的

+

法线

+
+

法线一般用切线空间存储,优点:自由度高,uv动画扰动,可以重用,可以压缩(只存储两个方向的数据)
+切线空间(右手坐标系): 切线方向(X轴)(和uv的u方向相同,有的是和v方向相同),次法线方向(Y轴),法线方向(Z轴)

+
+
    half3 normal_data=UnpackNormal(normalmap);
+    float3x3 TBN=float3x3(tangent_dir,binormal_dir,normal_dir);
+    normal_dir=normalize(mul(normal_data.xyz,TBN));
+    // 和上面矩阵相乘结果一样
+    //normal_dir=normalize(tangent_dir*normal_data.x+binormal_dir*normal_data.y+normal_dir*normal_data.z);
+
+

纹理

+
    +
  1. 漫反射纹理
  2. +
  3. 法线纹理
  4. +
  5. 渐变纹理(卡通风格,从冷色调到暖色调)
  6. +
  7. u方向渐变纹理
  8. +
  9. LUT(查找表,lookup table)纹理
  10. +
  11. 遮罩纹理
  12. +
  13. 高光强度遮罩
  14. +
  15. 高光指数遮罩
  16. +
  17. 边缘光强度遮罩
  18. +
  19. 自发光遮罩
  20. +
  21. 立方体纹理(天空盒)
  22. +
  23. 渲染纹理(渲染目标纹理,RT)
  24. +
  25. 程序纹理
  26. +
  27. AO贴图 + AO贴图会根据模型某一部分相对于其他组成部分或者其他模型之间的几何距离,模拟模型的光影效果,比如一些夹角会更暗或者更亮,某一些面因为其他模型部分的影响,可能光照更少。
  28. +
  29. Height贴图,高度图,视差贴图(视差偏移,视差映射) + height贴图会给模型本身根据实际需要增加凸起或者凹陷的几何效果。比如木质模型,某处被敲打而导致的凹陷效果。
  30. +
  31. 粗糙度贴图
  32. +
  33. 动画纹理(VAT)
  34. +
  35. FlowMap
  36. +
+

光照

+
    +
  • 漫反射:
      +
    • lambert:max(0,dot(n,l))
    • +
    • halflambert:dot(n,l)*0.5+0.5 +diffuse=basecolor*lightcolor*lambert ( or halflambert )
    • +
    +
  • +
  • 镜面反射(高光,各向异性高光,kk高光):
      +
    • phong:pow(max(dot(v,r),0),_Gloss)
    • +
    • blinn-phong:pow(max(dot(n,h),0),_Gloss) //性能比phong要好
    • +
    • speccolor=lightcolor_SpecIntensitylambert*blinn-phong ( or phong )
    • +
    +
  • +
  • 间接光漫反射:可以用 光照探针(light Probe) ,使用 SH 球谐光照模拟
  • +
  • 间接光镜面反射:可以用 反射探针(reflection Probe),使用 IBL (基于图像的照明)模拟
  • +
  • 菲涅尔: + float fresnel=_FresnelScale+(1-_FresnelScale)*pow(1-saturate(ndotv),_FresnelPower);
    +float reflectionFactor = _FresnelBias + _FresnelScale * pow(1 + dot(i, n), _FresnelPower);
  • +
  • 环境光(ambient): 环境光可以理解为间接光的一部分(可以用 间接光漫反射间接光镜面反射代替)
  • +
  • half3 ambient_color = UNITY_LIGHTMODEL_AMBIENT.rgb * base_color.xyz;
  • +
  • 自发光
  • +
  • Matcap: float2 uv_mapcap=(vNormal*0.5+0.5).xy;使用观察空间下的法线代表uv坐标
    +
    +20210410192004
    +

    Phong 光照模型: max(dot(n,l),0)+pow(max(dot(v,r),0),smoothness)+ambient=Phong
    +基础光照模型=直接光漫反射(Direct Diffuse)+直接光镜面反射(Direct Specular)+间接光漫反射(Indirect Diffuse)+间接光镜面反射(Indirect Specular)
    +直接光镜面反射: PBR中的GGX光照模型

    +
    +
  • +
+

环境贴图

+

环境贴图 (存储环境光或者间接光的漫反射和镜面反射的图像载体),环境光可以理解为间接光的一部分

+

环境贴图一般转成立方体贴图(Cubemap)使用,原因:直接采样环境贴图会造成贴图空间的浪费及采样会出现失真情况,所以先转成立方体贴图

+

Cubemap立方体贴图的局限性: 只根据方向来采样 Cubemap 会造成采样点错误,这也是为什么Cubemap技术不适合用于平面模型作反射的原因

+

立方体贴图(Cubemap)采样:texCUBE(_CubeMap, reflect_dir)

+


+20210525192856
+
+20210410230114

+
    +
  • CubeMap
  • +
+
            samplerCUBE _CubeMap;
+            float4 _CubeMap_HDR;  
+                half3 view_dir = normalize(_WorldSpaceCameraPos.xyz - i.pos_world);
+                half3 reflect_dir = reflect(-view_dir, normal_dir);
+                half4 color_cubemap = texCUBE(_CubeMap, reflect_dir);
+                half3 env_color = DecodeHDR(color_cubemap, _CubeMap_HDR);//确保在移动端能拿到HDR信息
+
+
    +
  • IBL_Specular
  • +
+
samplerCUBE _CubeMap;
+float4 _CubeMap_HDR;
+
+                half3 reflect_view_dir = reflect(-view_dir, normal_dir);
+
+                float roughness = tex2D(_RoughnessMap, i.uv);
+                roughness = saturate(pow(roughness, _RoughnessContrast) * _RoughnessBrightness);
+                roughness = lerp(_RoughnessMin, _RoughnessMax, roughness);
+                roughness = roughness * (1.7 - 0.7 * roughness);
+                float mip_level = roughness * 6.0;
+
+                half4 color_cubemap = texCUBElod(_CubeMap, float4(reflect_view_dir, mip_level));
+                half3 env_color = DecodeHDR(color_cubemap, _CubeMap_HDR);//确保在移动端能拿到HDR信息
+
+
    +
  • IBL_Diffuse
  • +
+
            samplerCUBE _CubeMap;
+            float4 _CubeMap_HDR;
+
+                float roughness = tex2D(_RoughnessMap, i.uv);
+                roughness = saturate(pow(roughness, _RoughnessContrast) * _RoughnessBrightness);
+                roughness = lerp(_RoughnessMin, _RoughnessMax, roughness);
+                roughness = roughness * (1.7 - 0.7 * roughness);
+                float mip_level = roughness * 6.0;
+                float4 uv_ibl = float4(normal_dir, mip_level);
+                half4 color_cubemap = texCUBElod(_CubeMap, uv_ibl);
+                half3 env_color = DecodeHDR(color_cubemap, _CubeMap_HDR);//确保在移动端能拿到HDR信息
+                half3 final_color = env_color * ao * _Tint.rgb * _Tint.rgb * _Expose;
+
+
    +
  • IBL_Reflection-Probe(环境光镜面反射)(unity捕捉生成的,unity最多支持两个反射探针)
  • +
+
                half3 view_dir = normalize(_WorldSpaceCameraPos.xyz - i.pos_world);
+                half3 reflect_view_dir = reflect(-view_dir, normal_dir);
+
+                reflect_view_dir = RotateAround(_Rotate, reflect_view_dir);
+
+                float roughness = tex2D(_RoughnessMap, i.uv);
+                roughness = saturate(pow(roughness, _RoughnessContrast) * _RoughnessBrightness);
+                roughness = lerp(_RoughnessMin, _RoughnessMax, roughness);
+                roughness = roughness * (1.7 - 0.7 * roughness);
+                float mip_level = roughness * 6.0;
+
+
+                half4 color_cubemap = UNITY_SAMPLE_TEXCUBE_LOD(unity_SpecCube0, reflect_view_dir, mip_level);
+                half3 env_color = DecodeHDR(color_cubemap, unity_SpecCube0_HDR);//确保在移动端能拿到HDR信息
+
+
    +
  • IBL_Light-Probe(环境光漫反射,内部使用SH读取)
  • +
+
    half3 env_color = ShadeSH9(float4(normal_dir,1.0)); //unity 内置函数
+
+
    +
  • SH球谐光照(环境光漫反射可以使用SH)(可以替代IBL_Diffuse,节省性能,不用读取cube贴图)
  • +
+
                float4 normalForSH = float4(normal_dir, 1.0);
+                //SHEvalLinearL0L1
+                half3 x;
+                x.r = dot(custom_SHAr, normalForSH);
+                x.g = dot(custom_SHAg, normalForSH);
+                x.b = dot(custom_SHAb, normalForSH);
+
+                //SHEvalLinearL2
+                half3 x1, x2;
+                // 4 of the quadratic (L2) polynomials
+                half4 vB = normalForSH.xyzz * normalForSH.yzzx;
+                x1.r = dot(custom_SHBr, vB);
+                x1.g = dot(custom_SHBg, vB);
+                x1.b = dot(custom_SHBb, vB);
+
+                // Final (5th) quadratic (L2) polynomial
+                half vC = normalForSH.x*normalForSH.x - normalForSH.y*normalForSH.y;
+                x2 = custom_SHC.rgb * vC;
+
+                float3 sh = max(float3(0.0, 0.0, 0.0), (x + x1 + x2));
+
+

+

光照衰减

+
+

光照衰减计算量太大,unity使用查找表(LUT,lookup table)纹理存储衰减数据(_LightTexture0),如果光源使用了cookie,则使用衰减查找纹理_LightTextureB0。

+
+
    # ifdef USING_DIRECTIONAL_LIGHT
+        fixed atten=1.0;
+    #else
+        float3 lightcoord = mul(unity_WorldToLight,float4(i.worldPosition,1)).xyz;
+        fixed atten=tex2D(_LightTexture0,dot(lightcoord,lightcoord).rr).r; //r equal UNITY_ATTEN_CHANNEL not cookie
+    #endif
+
+
+

一种简单的做法 点光源

+
    # ifdef USING_DIRECTIONAL_LIGHT
+        //half3 light_dir=normalize(_WorldSpaceLightPos0.xyz);
+        fixed atten=1.0;
+    #else
+        //half3 light_dir=normalize(_WorldSpaceLightPos0.xyz-i.pos_world);
+        half distance=length(_WorldSpaceLightPos0.xyz-i.pos_world);
+        half range=1.0/untiy_WorldToLight[0][0]; //光源范围
+        fixed atten=saturate((range-distance)/range);
+    #endif
+    half3 light_dir=normalize( lerp(_WorldSpaceLightPos0.xyz,_WorldSpaceLightPos0.xyz-i.pos_world,_WorldSpaceLightPos0.w));
+
+

阴影(Shadow)

+
+

阴影映射纹理(深度纹理)存储距离光源的深度信息
+老版本是在光源空间中计算深度数据
+新版本部分平台是在屏幕空间中计算深度数据,显卡必须支持MRT才行

+
+


+20210410223031
+
+20210410223441

+

角色渲染

+


+20210411012026

+

动画

+

uv动画

+

顶点动画

+
1. 顶点动画贴图(VAT)  (风力动画可以用这种)`比较细腻`(旗帜燃烧CS06)
+
+
+

+ + + 2.

+
+

关键帧动画

+

骨骼动画

+

屏幕后处理

+

+
    +
  • +

    亮度
    +fixed3 finalColor=baseCol.rgb*_Brightness

    +
  • +
  • +

    饱和度

    +
  • +
+
    fixed luminance=0.2125*baseCol.r+0.7154*baseCol.g+0.0721*baseCol.b;
+    fixed3 luminanceCol=fixed(luminance,luminance,luminance);
+    finalCol = lerp(luminanceCol,finalCol,_Saturation); 
+
+
    +
  • 对比度
  • +
+
    fixed3 avgColor=fixed3(0.5,0.5,0.5);
+    finalCol=lerp(avgColor,finalCol,_Constrast);
+
+
    +
  • 晕影/暗角(Vignette)
  • +
+
    //暗角/晕影
+    float2 d=abs(i.uv-half2(0.5,0.5))*_VignetteIntensity;
+    d=pow(saturate(d),_VignetteRoundness);
+    float dist=length(d);
+    float vfactor=pow(saturate(1.0-dist*dist),_VignetteSmoothness);
+
+

边缘检测

+

模糊(Blur)

+
+

参考:高品质后处理:十种图像模糊算法的总结与实现

+
+

+
方框模糊(Box Blur)
+
高斯模糊(Gaussian Blur)
+
双重模糊(Dual Blur)
+
Kawase模糊(DualKawaseBlur)
+
运动模糊
+

泛光 (Bloom)

+

光晕

+

深度纹理和法线纹理

+
+

设置 camera.depthTextureMode=DepthTextureMode.DepthNormals;
+_CameraDepthTexture

+
+

全局雾效

+

卡通风格渲染

+

素描风格渲染

+

色调映射(Tone-Mapping)

+

用Tone-Mapping压缩高光范围 +HDR颜色通过色调映射转到(0-1)范围内 +一般用于屏幕后处理

+
    // Tone-Mapping 需要将x从Gamma空间转到Lear线性空间使用,结果再转到Gamma空间下
+    inline float3 ACESFilm(float3 x)
+    {
+        float a=2.51f;
+        float b= 0.03f;
+        float c=2.43f;
+        float d=0.59f;
+        float e=0.14f;
+        return saturate((x*(a*x+b))/(x*(c*x+d)+e))
+    }
+
+    // Gamma空间 转Lear空间 color_lear=pow(color_gamma,2.2);
+    // Lear空间转Gamma空间 color_gamma=pow(color_lear,1.0/2.2);
+
+

+

渲染优化

+

开销成因

+
    +
  1. CPU
  2. +
  3. 过多的drawcall
  4. +
  5. 复杂的脚本或者物理模拟
  6. +
  7. GPU
  8. +
  9. 顶点处理
      +
    1. 过多的顶点
    2. +
    3. 过多的逐顶点计算
    4. +
    +
  10. +
  11. 片元处理
      +
    1. 过多的片元(可能分辨率高或者overdraw)
    2. +
    3. 过多的逐片元计算
    4. +
    +
  12. +
  13. 带宽
  14. +
  15. 使用尺寸很大且未压缩的纹理
  16. +
  17. 分辨率过高的帧缓存
  18. +
+

优化方案

+
+

【Unity技巧】Unity中的优化技术 +1. CPU + 1. 静态批处理(static batching)降低drawcall + 2. 动态批处理(顶点属性小于900(如果使用顶点坐标,法线和纹理坐标则顶点数量小于300),lightmap必须参数相同指向同一位置,多pass打断合并)降低drawcall + 3. 使用图集 + 4. 共享材质 +2. GPU + 1. 减少顶点数量 + 1. 优化几何体 + 2. 使用模型lod(Level of Detail)技术(unity中使用LOD Group组件) + 3. 使用遮挡剔除(Occlusion Culling)技术 + 4. 使用mesh压缩 + 2. 减少片元数量(核心降低overdraw) + 1. 控制绘制顺序 + 2. 警惕透明物体 + 3. 减少实时光照和阴影 + 3. 减少计算复杂度 + 1. 使用Shader的LOD技术 + 1. 设置Shader.maximumLDO或者Shader.globalMaximumLOD来允许最大的LOD + 2. Shader代码优化 + 1. 把高斯模糊和边缘计算计算放到顶点shader中 + 2. float存储顶点坐标等变量,half存储一些标量和纹理坐标等信息,fixed适用于大多数颜色变量和归一化的方向矢量 +3. 节省内存带宽 + 1. 减少纹理大小 + 2. mipmap + 3. 关闭readwrite + 4. 纹理压缩(ETC2 8bit,ASTC 4x4 block,PVRTC) + 5. 降低屏幕分辨率

+

待处理 +7.4.2 遮罩纹理的使用 data2 +着色器替换技术(Shader Replacement) +

+

常用素材资料:
+https://sketchfab.com/

+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git "a/ANote/Shader\347\254\224\350\256\2602/index.html" "b/ANote/Shader\347\254\224\350\256\2602/index.html" new file mode 100644 index 00000000..9e736689 --- /dev/null +++ "b/ANote/Shader\347\254\224\350\256\2602/index.html" @@ -0,0 +1,8738 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Shader笔记2 - 王国庆的博客 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + 跳转至 + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ +
+ + + + + + + + + +

Shader笔记2

+

渲染相关问题(Problem)

+
    +
  • [x] BRDF
  • +
  • [x] GPU Instancing
  • +
  • [x] SRPBatcher
  • +
  • [ ] Shadow,联级阴影
  • +
+

tex2Dproj和tex2D的区别

+
+

ref tex2Dproj和tex2D的区别 +tex2Dproj和tex2D这两个功能几乎相同。

+
+

唯一的区别是,在对纹理进行采样之前,tex2Dproj将输入的UV xy坐标除以其w坐标。这是将坐标从正交投影转换为透视投影。

+

例如 以下段代码的返回值是相同的.

+

float existingDepth01 = tex2Dproj(_CameraDepthTexture, UNITY_PROJ_COORD(i.screenPosition)).r;

+

float existingDepth01 = tex2D(_CameraDepthTexture, UNITY_PROJ_COORD(i.screenPosition.xy / i.screenPosition.w)).r;

+
+

具体什么情况下使用tex2Dproj呢?
+我们知道,裁剪空间的坐标经过缩放和偏移后就变成了(0,w),而当分量除以分量W以后,就变成了(0,1),这样在计算需要返回(0,1)值的时候,就可以直接使用tex2Dproj了.

+
+

深度图基础及应用

+
+

ref Unity Shader - 深度图基础及应用
+ref 神奇的深度图:复杂的效果,不复杂的原理
+ 章节:ShaderLearn-CS61

+
+
    +
  • [x] 渲染深度图
  • +
  • [x] 相交高亮
  • +
  • 能量场
  • +
  • [x] 全局雾效
  • +
  • [x] 扫描线
  • +
  • 水淹
  • +
  • [x] 垂直雾效
  • +
  • 边缘检测
  • +
  • 运动模糊
  • +
  • 景深
  • +
  • [x] 透过墙壁绘制背后的“人影”
  • +
+

Depth实现特效实现概览

+

DepthTextureMode.Depth

+

深度会被保存在 _CameraDepthTexture 中,可以通过screenpos坐标来获取贴图中的值,然后使用 UNITY_SAMPLE_DEPTH 宏来提取深度(经常指向r通道)。

+

需要注意的是,深度纹理使用了与阴影投射(shadow caster)相同的Shader pass,所以如果一个对象不支持阴影投射,那么它将不会出现在深度纹理当中,并且只有RenderQueue小于等于2500的对象才能被渲染到深度纹理当中去。

+
    float2 uv = i.uv;
+    float depth = UNITY_SAMPLE_DEPTH(tex2D(_CameraDepthTexture, uv));
+
+

DepthTextureMode.DepthNormals

+

带有Normalsdepth 的的32位贴图,Normals根据Stereographic projection编码到R&G通道,Depth通过映射编码到 B&A 通道。Unity ShaderLab也提供DecodeDepthNormal 方法进行解码,其中深度是0~1范围。

+

Normals & Depth Texture 是通过 Camera Shader replacement 实现,可以将RenderType为:Opaque、TransparentCutout、TreeBark、TreeLeaf、TreeOpaque、TreeTransparentCutout、TreeBillboard、GrassBillboard、Grass类型才会进行深度渲染,对于Transparent\AlphaTest是不会渲染到这个纹理中。详情可参考:浅析Unity shader中RenderType的作用及_CameraDepthNormalsTexture

+

在使用中,需提前定义 _CameraDepthNormalsTexture ,使用DecodeDepthNormal解码,需要注意的是:深度是0~1范围,和_CameraDepthTexture 有区别。

+
    float2 uv = i.uv;
+    half depth;
+    half3 norm;
+    DecodeDepthNormal(tex2D(_CameraDepthNormalTexture, uv), depth, norm);
+
+
+

入门版水面实现

+
    +
  • 使用相机做一张反射图,使用屏幕坐标作为UV进行采样
  • +
  • +

    使用水的法线做出双层法线然后相加得出世界空间法线

    +
  • +
  • +

    用计算出的法线除以投影空间下的w(z值),做出近大远小的法线扰动(z值越远越大,作为被除数使得法线近大远小)然后和采样UV相加进行UV扰动

    +
  • +
  • 增加水底使用视差贴图
  • +
  • 增加阳光照到水面BlinBlin闪闪的效果(将HDR部分扣除使用Bloom)
  • +
+

升级版海边

+
+

CS18 +1. 水体颜色(Water color) +3. 镜面反射(高光) +3. 折射/水底(Refrection) +4. 焦散 +4. 海浪(Wave) +5. 岸边泡沫(Foam) +6. 水体运动(顶点动画)

+
+

第十五章 天空和水面

+
    +
  • [x] 采样纹理颜色后进行减法操作,减2,然后取大于0的值,
  • +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git "a/ANote/Shell\347\254\224\350\256\260/index.html" "b/ANote/Shell\347\254\224\350\256\260/index.html" new file mode 100644 index 00000000..0f8e2a8f --- /dev/null +++ "b/ANote/Shell\347\254\224\350\256\260/index.html" @@ -0,0 +1,9658 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Shell笔记 - 王国庆的博客 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + 跳转至 + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ +
+ + + + + + + + + +

Shell笔记

+

常用快捷键

+

查看环境变量

+

env | less -N

+

printenv | less -N

+

打印变量

+
echo $?
+
+# mac or linux
+echo $PATH
+
+# windows
+echo %PATH%
+
+

显示shell变量

+

set | less -N

+

打印变量

+
echo $?
+
+echo $TERM
+
+
+

unset 复位变量

+
+

whereis 和 whatis

+
[root@VM-24-15-centos ~]# whatis cp
+cp (1)               - copy files and directories
+[root@VM-24-15-centos ~]# whereis cp
+cp: /usr/bin/cp /usr/share/man/man1/cp.1.gz
+[root@VM-24-15-centos ~]# 
+
+
+

who am i

+
mrwang@CodingdeMBP hugo-project % who am i 
+mrwang   ttys000  Feb  3 12:43 
+mrwang@CodingdeMBP hugo-project % who are you
+mrwang   ttys000  Feb  3 12:44 
+mrwang@CodingdeMBP hugo-project % who goes there
+mrwang   ttys000  Feb  3 12:44 
+mrwang@CodingdeMBP hugo-project % who is god
+mrwang   ttys000  Feb  3 12:44 
+
+

lsof

+
+

lsof(list open files)是一个列出当前系统打开文件的工具。

+
+

lsof 查看端口占用语法格式:

+
lsof -i:端口号
+# lsof -i 需要 root 用户的权限来执行
+
+# lsof -i:8000
+COMMAND   PID USER   FD   TYPE   DEVICE SIZE/OFF NODE NAME
+nodejs  26993 root   10u  IPv4 37999514      0t0  TCP *:8000 (LISTEN)
+# 可以看到 8000 端口已经被轻 nodejs 服务占用。
+
+

更多 lsof 的命令如下:

+
lsof -i:8080:查看8080端口占用
+lsof abc.txt:显示开启文件abc.txt的进程
+lsof -c abc:显示abc进程现在打开的文件
+lsof -c -p 1234:列出进程号为1234的进程所打开的文件
+lsof -g gid:显示归属gid的进程情况
+lsof +d /usr/local/:显示目录下被进程开启的文件
+lsof +D /usr/local/:同上,但是会搜索目录下的目录,时间较长
+lsof -d 4:显示使用fd为4的进程
+lsof -i -U:显示所有打开的端口和UNIX domain文件
+
+

netstat

+
+

netstat -tunlp 用于显示 tcp,udp 的端口和进程等相关情况。网络查询

+
+
# netstat 查看端口占用语法格式:
+netstat -tunlp | grep 端口号
+
+netstat -ntlp   //查看当前所有tcp端口
+netstat -ntulp | grep 80   //查看所有80端口使用情况
+netstat -ntulp | grep 3306   //查看所有3306端口使用情况
+
+-t (tcp) 仅显示tcp相关选项
+-u (udp)仅显示udp相关选项
+-n 拒绝显示别名,能显示数字的全部转化为数字
+-l 仅列出在Listen(监听)的服务状态
+-p 显示建立相关链接的程序名
+
+
+# netstat -tunlp | grep 8000
+tcp        0      0 0.0.0.0:8000            0.0.0.0:*               LISTEN      26993/nodejs  
+
+

kill

+
+

在查到端口占用的进程后,如果你要杀掉对应的进程可以使用 kill 命令:

+
+
kill -9 PID
+
+# 如上实例,我们看到 8000 端口对应的 PID 为 26993,使用以下命令杀死进程:
+kill -9 26993
+
+

tar

+

解压缩

+
tar -zxvf a.tar.gz
+
+

键入信号:erase, werase, kill

+ + + + + + + + + + + + + + + + + + + + + + + + + +
信号作用
erase< Backspace > / < Delete >删除最后一个键入的字符
werase^W删除最后一个键入的单词
kill^X / ^U删除整行
+

^X 将光标移动到行的开头位置

+

stty命令

+
## 显示所有键盘映射当前设置
+stty -a
+
+## 屏蔽显示
+stty -echo #禁止回显
+stty echo #打开回显
+
+## 忽略回车符
+stty igncr  # 开启
+stty -igncr # 恢复
+
+## 改变ctrl+D的方法:
+stty eof "string" # 系统默认是ctrl+D来表示文件的结束,而通过这种方法,可以改变! 
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
信号作用
erase< Backspace > / < Delete >删除最后一个键入的字符
werase^W删除最后一个键入的单词
kill^X / ^U删除整行
stop^S暂停屏幕显示
start^Q重新启动屏幕显示
eof^D指示已经没有数据
+

less 命令

+

less 与 more 类似,但使用 less 可以随意浏览文件,而 more 仅能向前移动,却不能向后移动,而且 less 在查看之前不会加载整个文件。

+

+b  向上翻一页
+空格键 向下翻一页
+y  向上滚动一行
+回车键 向下滚动一行
+u  向上翻半页
+d  向下翻半页
+
+
+-i  忽略搜索时的大小写
+-N  显示每行的行号
+-o  <文件名> 将less 输出的内容在指定文件中保存起来
+-s  显示连续空行为一行
+/字符串:向下搜索“字符串”的功能
+?字符串:向上搜索“字符串”的功能
+n:重复前一个搜索(与 / 或 ? 有关)
+N:反向重复前一个搜索(与 / 或 ? 有关)
+-x <数字> 将“tab”键显示为规定的数字空格
+h  显示帮助界面
+Q  退出less 命令
+[pagedown]: 向下翻动一页
+[pageup]:   向上翻动一页
+
+
    +
  • 案例一 分页查看ps进程信息
  • +
+
## -N 显示行号
+ps -aux | less -N
+
+
    +
  • 案例二 查看多个文件
  • +
+
## 可以使用 n 查看下一个,使用 p 查看前一个。
+less 1.log 2.log
+
+

scp 命令

+
+

scp是secure copy的简写,用于在Linux下进行远程拷贝文件的命令,和它类似的命令有cp,不过cp只是在本机进行拷贝不能跨服务器,而且scp传输是加密的。可能会稍微影响一下速度。当你服务器硬盘变为只读 read only system时,用scp可以帮你把文件移出来。另外,scp还非常不占资源,不会提高多少系统负荷,在这一点上,rsync就远远不及它了。虽然 rsync比scp会快一点,但当小文件众多的情况下,rsync会导致硬盘I/O非常高,而scp基本不影响系统正常使用。

+
+

使用scp需要远程用户密码;如果配置远程ssh的私钥则不用密码,自动使用私钥登陆

+

命令格式

+
scp [参数] [原路径] [目标路径]
+[目标路径]: user@server-ip:server-path
+
+

常用命令参数

+
-C - 这会在复制过程中压缩文件或目录。
+-P - 如果默认 SSH 端口不是 22,则使用此选项指定 SSH 端口。
+-r - 此选项递归复制目录及其内容。
+-p - 保留文件的访问和修改时间。
+
+
    +
  • 复制文件到远程服务器
  • +
+
scp logs.tar.gz root@192.168.43.137:/root
+
+
    +
  • 复制远程服务器文件到本地
  • +
+
scp  root@192.168.43.137:/root/logs.tar.gz ./
+
+
    +
  • 复制文件夹到远程服务器
  • +
+
scp -rC syslog root@192.168.43.137:/root
+
+
    +
  • 复制远程服务器文件夹到本地
  • +
+
scp -rC  root@192.168.43.137:/root syslog
+
+
+

参考:
+https://www.cnblogs.com/peida/archive/2013/03/15/2960802.html
+https://www.linuxprobe.com/scp-cmd-usage.html

+
+

rsync 命令

+
+

Rsync(remote sync ; remote synchronous)是UNIX 及类UNIX 平台下一款神奇的数据镜像备份软件,它不像FTP 或其他文件传输服务那样需要进行全备份,Rsync 可以根据数据的变化进行差异备份,从而减少数据流量,提高工作效率。你可以使用它进行本地数据或远程数据的复制,Rsync 可以使用SSH 安全隧道进行加密数据传输。Rsync 服务器端定义源数据,Rsync 客户端仅在源数据发生改变后才会从服务器上实际复制数据至本地,如果源数据在服务器端被删除,则客户端数据也会被删除,以确保主机之间的数据是同步的。Rsync 使用TCP 873 端口。 +使用rsync需要远程用户密码;如果配置远程ssh的私钥则不用密码,自动使用私钥登陆 +配置文件:/etc/rsyncd.conf

+

windows安装 rsync +https://www.itefix.net/cwrsync
+https://www.cnblogs.com/zhangweiyi/p/10571273.html
+https://blog.csdn.net/zetion_3/article/details/103575905

+
+

常用参数:

+
-a 包含-rtplgoD
+-r 同步目录时要加上,类似cp时的-r选项
+-v 同步时显示一些信息,让我们知道同步的过程
+-l 保留软连接
+-L 加上该选项后,同步软链接时会把源文件给同步
+-p 保持文件的权限属性
+-o 保持文件的属主
+-g 保持文件的属组
+-D 保持设备文件信息
+-t 保持文件的时间属性
+--delete 删除DEST中SRC没有的文件
+--exclude 过滤指定文件,如--exclude “logs”会把文件名包含logs的文件或者目录过滤掉,不同步
+-P 显示同步过程,比如速率,比-v更加详细
+-u 加上该选项后,如果DEST中的文件比SRC新,则不同步
+-z 传输时压缩
+
+

sz,rz 命令

+
+

使用xshell来操作服务非常方便,传文件也比较方便。 +就是使用rz,sz

+
+

首先,服务器要安装了rz,sz +yum install lrzsz

+

当然你的本地windows主机也通过ssh连接了linux服务器 +运行rz,会将windows的文件传到linux服务器 +运行sz filename,会将文件下载到windows本地

+

同步文件夹

+
    +
  • 上传文件夹及同步
  • +
+
## -r 递归处理文件夹
+## 文件夹最后必须带斜杠(/),不带则远程会创建文件夹
+## 第一次同步后,第二次及后面的同步都是增量同步,存在的文件且相同的不会传输
+rsync -r ~/pub/ 101.43.160.247:~/public/
+
+## --delete 本地如果删除文件或文件夹,同步后会删除远程的文件或文件夹(exclude 忽略的文件或文件夹除外)
+## --exclude 本地忽略同步的文件或文件夹,例如项目中.git文件夹不需要同步到远程
+rsync -rtvpz --delete --exclude .git ~/pub/ root@101.43.160.247:~/public/
+
+rsync -rt --delete --exclude .git ~/pub/ 101.43.160.247:~/public/
+
+
+
    +
  • 下载文件夹及同步
  • +
+
## -r 递归处理文件夹
+## 文件夹最后必须带斜杠(/),不带则远程会创建文件夹
+## 第一次同步后,第二次及后面的同步都是增量同步,存在的文件且相同的不会传输
+rsync -r 101.43.160.247:~/public/ ~/pub/ 
+
+## --delete 本地如果删除文件或文件夹,同步后会删除远程的文件或文件夹(exclude 忽略的文件或文件夹除外)
+## --exclude 本地忽略同步的文件或文件夹,例如项目中.git文件夹不需要同步到远程
+rsync -rtvpz --delete --exclude .git 101.43.160.247:~/public/ ~/pub/ 
+
+
+

同步文件

+
    +
  • 上传文件及同步
  • +
+
rsync  ~/pub/a.txt 101.43.160.247:~/public/f.txt 
+
+
+
    +
  • 下载文件及同步
  • +
+
rsync  101.43.160.247:~/public/f.txt ~/pub/a.txt
+
+
+
+

参考:
+https://www.jianshu.com/p/5a799b36c7e1
+https://blog.csdn.net/allway2/article/details/103073243
+Rsync同步时删除多余文件
+rsync --exclude 参数
+windows 上rsync客户端使用方法

+
+

md5sum 使用

+
+

centos 默认安装了md5sum命令

+
+
    +
  • 计算二进制文件的md5:
  • +
+
# -b, --binary         Read files in binary mode
+# -t, --text           Read files in ASCII mode
+md5sum  filename
+
+
    +
  • 计算字符串md5值:
  • +
+
[root@xyz.com ~]$ echo -n 'hello world!' | md5sum
+fc3ff98e8c6a0d3087d515c0473f8677  -
+
+

注:一定要加上'-n'参数,代表去掉控制字符。

+

错误命令示例1:

+
[root@xyz.com ~]$ echo 'hello world!' | md5sum
+c897d1410af8f2c74fba11b1db511e9e  -
+
+

错误操作2:将文本hello world!写在文本文件中进行保存test文件,然后对文件进行md5sum

+
mrwang@CodingdeMBP Sites % echo 'hello world!' | md5sum 
+c897d1410af8f2c74fba11b1db511e9e  -
+mrwang@CodingdeMBP Sites % cat test | md5sum
+c897d1410af8f2c74fba11b1db511e9e  -
+mrwang@CodingdeMBP Sites % md5sum test
+c897d1410af8f2c74fba11b1db511e9e  test
+mrwang@CodingdeMBP Sites % 
+
+

此错误操作与错误1得到的结果一样,都是因为文本中会自动带上一些控制字符,从而导致最终计算出来的md5值不是纯粹字符串的md5值。

+

用vi打开test文件 使用vi 命令":set list",显示如下:

+
hello world!$
+
+

多了控制字符。

+
    +
  • 批量文件计算md5:
  • +
+
#!/bin/sh
+
+#获取文件夹下所有文件
+folder="./"
+
+softfiles=$(ls $folder)
+cd ${folder}
+for sfile in ${softfiles}
+do
+    md5sum $sfile >> ../md5sum.txt
+    # md5sum $sfile
+done
+
+
+

Mac 使用 md5sum:
+Mac没有自带md5sum, 需要安装md5sum
+使用brew安装 brew install md5sha1sum

+

参考:
+https://www.cnblogs.com/xd502djj/p/7055228.html +http://www.blogjava.net/anchor110/articles/433319.html

+
+

find 命令

+

查找文件或者文件夹test

+
# 在当前目录查找文件或文件夹 test
+find test
+# 在根目录查找文件或文件夹 test
+find / -name test
+# 在根目录查找文件或文件夹 以test开头的
+find / -name test*
+find / -name ‘test*’
+# 在 home 目录查找文件或文件夹 包含 test 的
+find /home -name *test*
+find /home -name ‘*test*’
+
+
+

查找文件test

+
# 在当前目录查找文件 test
+find test -type f
+# 在根目录查找文件 test
+find / -name test -type -f
+
+

查找文件夹test

+
# 在当前目录查找文件夹 test
+find test -type f
+# 在根目录查找文件夹 test
+find / -name test -type -f
+
+
+

参考:
+https://www.runoob.com/linux/linux-comm-find.html
+https://blog.csdn.net/l_liangkk/article/details/81294260

+
+

tail 命令

+

Linux中用于查看文件尾部的内容,与head相对应。
+常用来查看日志文件,通过 tail -f 实时查看文件最新内容。

+

尤其是对于日志文件较大的时候,通过tail指定输出的行数来查看日志。

+
// 输出最后10行的内容
+tail test.log
+
+// 输出最后10行的内容,同时监视文件的变化,一旦变化就显示出来
+tail -f test.log
+
+// 输出最后n行的内容,同时监视文件的变化,一旦变化就显示出来
+tail -nf test.log
+
+// 输出文件最后10行的内容
+tail -n 10 filename
+// 除第9行不显示外,显示第10行到末尾行
+tail -n -10 filename
+
+// 从第20行至末尾
+tail +20 test.log
+
+// 显示最后10个字符
+tail -c 10 test.log
+
+// 实时日志查看与grep过滤关键字
+// -A 除显示符合t匹配内容的那一行之外,并显示该行之后的内容
+// -B 除显示符合匹配内容的那一行之外,并显示该行之前的内容
+// -C 除显示符合匹配内容的那一列之外,并显示该列前后的内容
+tail -f test.log | grep 'test' -C 5
+tail -f test.log | grep 'test' -5
+
+
+

参考:https://blog.csdn.net/mo_247/article/details/103567545

+
+

curl 命令

+
+

https://www.cnblogs.com/duhuo/p/5695256.html
+https://www.ruanyifeng.com/blog/2019/09/curl-reference.html

+
+

常用命令

+

读取文件每一行

+
    +
  1. 使用for循环
  2. +
+
# 换行符必须是unix的\n,不能是windows的\r\n,mac是\r结尾
+# `cat img.txt | tr "\r\n" "\n" | tr "\r" "\n"`
+for line in `cat img.txt | tr '\r\n' '\n'`
+do
+  echo $line
+  cp -f "/d/Sites/hugo-project/content/imgs/${line}" "./imgs/${line}"
+done
+
+
    +
  1. 使用for循环
  2. +
+
# 换行符必须是unix的\n,不能是windows的\r\n,mac是\r结尾
+# $(cat img.txt | tr "\r\n" "\n" | tr "\r" "\n")
+for line in $(cat img.txt| tr '\r\n' '\n')
+do
+  echo $line
+  cp -f "/d/Sites/hugo-project/content/imgs/${line}" "./imgs/${line}"
+done
+
+
    +
  1. 使用while循环
  2. +
+
# 换行符必须是unix的\n,不能是windows的\r\n,mac是\r结尾
+while read -r line
+do
+  echo $line
+  cp -f "/d/Sites/hugo-project/content/imgs/${line}" "./imgs/${line}"
+done < filename
+
+
+

参考:https://www.cnblogs.com/oskb/p/9669186.html

+
+

ln 软连接

+

第一,ln命令会保持每一处链接文件的同步性,也就是说,不论你改动了哪一处,其它的文件都会发生相同的变化;

+

第二,ln的链接分软链接和硬链接两种,软链接就是ln–s ,它只会在你选定的位置上生成一个文件的镜像,不会占用磁盘空间,硬链接ln,没有参数-s, 它会在你选定的位置上生成一个和源文件大小相同的文件,无论是软链接还是硬链接,文件都保持同步变化。

+
# 源文件 和 链接名称 尽量使用绝对路径
+ln -s <源文件> <链接名称>
+
+
+ln-s abc cde    #建立abc 的软连接 
+ln abc cde       #建立abc的硬连接
+
+         -f:  链结时先将与dist同档名的档案删除
+
+        -d:允许系统管理者硬链结自己的目录
+
+        -i:  在删除与dist同档名的档案时先进行询问
+
+        -n:在进行软连结时,将dist视为一般的档案
+
+        -s:进行软链结(symboliclink)
+
+        -v:在连结之前显示其档名
+
+        -b:将在链结时会被覆写或删除的档案进行备份
+
+       -SSUFFIX :将备份的档案都加上 SUFFIX的字尾
+
+       -VMETHOD :指定备份的方式
+
+       --help:显示辅助说明
+
+       --version:显示版本
+
+
+

android adb命令

+
+

这里总结了一些常用的PC版模拟器连接方法!其他的自行搜索!
+MuMu模拟器:adb connect 127.0.0.1:7555
+夜神模拟器:adb connect 127.0.0.1:62001
+雷电模拟器:adb connect 127.0.0.1:5555
+逍遥安卓模拟器:adb connect 127.0.0.1:21503
+天天模拟器:adb connect 127.0.0.1:6555
+海马玩模拟器:adb connect 127.0.0.1:53001

+

https://www.52pojie.cn/thread-1872463-1-1.html

+
+
    +
  1. 查看设备列表 + adb devices + PS E:\Soft> adb devices + List of devices attached + f8d600b1 device + emulator-5554 device
  2. +
  3. 安装apk
  4. +
+
指定一个设备安装,apk要用绝对路径(可以直接拽到命令行中)
+PS E:\Soft> adb -s f8d600b1 install E:\Soft\debug.apk
+Performing Streamed Install
+Success
+
+如果devices只有一个设备,apk要用绝对路径(可以直接拽到命令行中)
+PS E:\Soft> adb install E:\Soft\debug.apk
+Performing Streamed Install
+Success
+
+强制安装(覆盖安装时使用)
+adb install -r E:\Soft\debug.apk
+
+

常见问题

+

情况一:adb devices命令启动了adb服务器,但是设备列表未显示。

+

解决方法:

+

第一步:先使用adb kill-server命令停止 adb 服务器,再切换目录到android_sdk/tools 目录下,因为emulator 命令位于 android_sdk/tools 目录下。

+

第二步:停止 adb 服务器后,输入命令emulator -list-avds获取 AVD 名称列表,再执行命令emulator -avd AVD名称 -port 奇数端口号,最后执行adb devices -l查询设备即可。

+

情况二:在下面的命令序列中,adb devices 显示了设备列表,因为先启动了 adb 服务器。

+

解决方法:

+

第一步:先停止adb服务器,再切换目录到android_sdk/tools 目录下使用命令emulator -avd AVD名称 -port 奇数端口号。

+

第二步:在使用adb devices -l命令查询设备之前,使用adb start-server命令重新启动adb服务器。

+

adb连接夜神多开器的多个模拟器

+

但是当我们打开多个夜神的其他多开设备时候, 直接使用以下命令链接,会失败!

+

adb connect 127.0.0.1:62001 +那么这个时候我们需要先关闭其他模拟器,只打开要链接的那个多开器,然后去cmd窗口输入

+

tasklist +

+

找到对应的夜神pid,然后输入

+

netstat -ano | findstr 5508 + +找到端口为 62***的,然后去adb链接 + +连接成功! +可以使用以下命令,查看一下!

+
adb devices
+
+

+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git "a/ANote/UI\347\254\224\350\256\260/index.html" "b/ANote/UI\347\254\224\350\256\260/index.html" new file mode 100644 index 00000000..13d8b6ef --- /dev/null +++ "b/ANote/UI\347\254\224\350\256\260/index.html" @@ -0,0 +1,8755 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + UI笔记 - 王国庆的博客 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + 跳转至 + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+ +
+ + + + + + + + + +

UI笔记

+

Problem

+
    +
  • [ ] RootScale来源,怎么计算的
  • +
+

UI适配

+

UGUI适配

+

UI特效适配及层级关系控制

+

UGUI合批

+
+

合批过程是指Canvas合并UI元素的网格,并且生成发送给Unity渲染管线的命令。Canvas使用的网格都是从绑定在Canvas上的CanvasRenderer获得,但是不包含子Canvas的网格。UGUI的层叠顺序是按照Hierarchy中的顺序从上往下进行的,也就是越靠上的组件,就会被绘制在越底部。所有相邻层的可合批的UI元素(具有相同材质和纹理),就会在一个DrawCall中完成。

+
+

测试

+
+

Win10,Unity 2019.4.17f1c1

+
+

+


+8个drawcall,左右的白色图片合批了,其他都没有合批;再次打开场景显示7个drawcall,左右白色图片、绿色图片合批了,其他都没有合批。

+

+

+

+

+

+ +白色图片不能合批,一共占用3个drawcall。

+
+

根据上图占用3个drawcall,推断出,Hierachy内的上下关系不能保证临近位置合批,受到前后遮挡关系影响。 +这里有个问题是 第1个drawcall先绘制的左侧的白图,根据Hierachy的顺序,应该先绘制蓝色图片才对?

+
+
    +
  • 设置Image的alpha=0,坐标移到画布视野外,还是会继续占用drawcall,设置localScale=0则不会占用drawcall
  • +
+

Depth计算算法:

+
    +
  • 遍历所有UI元素(已深度优先排序),对当前每一个UI元素CurrentUI,如果不渲染,CurrentUI.depth = -1,如果渲染该UI且底下没有其他UI元素与其相交(rect Intersects),其CurrentUI.depth = 0;
  • +
  • 如果CurrentUI下面只有一个的需要渲染的UI元素LowerUI与其相交,且可以Batch(material instance id 和 texture instance id 相同,即与CurrentUI具有相同的Material和Texture),CurrentUI.depth = LowerUI.depth;否则,CurrentUI.depth= LowerUI.depth + 1;
  • +
  • 如果CurrentUI下面叠了多个元素,这些元素的最大层是MaxLowerDepth,如果有多个元素的层都是MaxLowerDepth,那么CurrentUI和下面的元素是无法合批的;如果只有一个元素的层是MaxLowerDepth,并且这个元素和CurrentUI的材质、纹理相同,那么它们就能合批。
  • +
+

DrawCall合批(Batch):

+
    +
  • Depth计算完后,依次根据Depth、material ID、texture ID、RendererOrder(即UI层级队列顺序,HierarchyOrder)排序(条件的优先级依次递减),剔除depth == -1的UI元素,得到Batch前的UI元素队列VisiableList。
  • +
  • 对VisiableList中相邻且可以Batch(相同material和texture)的UI元素合并批次,然后再生成相应mesh数据进行绘制。
  • +
+

UGUI合批策略

+
    +
  • 设置窗口父节点Z 值为 0,否则下面的元素都无法合批了。子UI元素Z值不为0时,会被视为3D UI,不参与合批。如果有Z改变了,尽量通过Group来规整在一起
  • +
  • 设置Canvas.overrideSorting为true
  • +
  • 设置Canvas.sortingOrder为窗口在Hierarchy下的排列顺序,即每当打开一个新的窗口,都会+1,根据数值由小到大依次渲染。
  • +
  • 设置Canvas.sortingLayerID为0,表示默认为Default
  • +
  • 设置Canvas.sortingLayerName为SortingLayer.IDToName(0)
  • +
  • 根据功能适用性,合理使用Atlas图集。
  • +
  • Mask和RectMask2D组件, + >1. 不同mask之间是可以合批的 + >1. 被mask的物体只是不被绘制,依旧会影响合批计算 + >1. RectMask2D之间无法进行合批 + >1. 被RectMask2D隐藏的物体不会参与合批计算 + >1. RectMask2D组件上挂载的Image可以参与外部的合批
  • +
  • 不要使用Unity内置的素材,不能合批
  • +
  • GameObject.activeSelf 获取本地激活状态,需要注意的是当父对象是没有激活的时候,也会返回True
  • +
  • GameObject.activeInHierarchy 获取游戏对象在场景中的激活状态,当父对象是没有激活的时候,就会返回False
  • +
+
+

参考: +https://zhuanlan.zhihu.com/p/339387759
+https://www.cnblogs.com/moran-amos/p/13878493.html +https://www.cnblogs.com/moran-amos/p/13883818.html

+
+

UGUI优化指南

+
    +
  1. 优化填充率,裁减掉无用的区域,镂空等,镂空可以勾掉FillCenter。 +
  2. +
  3. Mask和RectMask2D的优劣,根据具体需求取舍。
  4. +
  5. 少用unity自带的outline和shadow。 + >会大量增加顶点和面数,比如outline,他实现原理是复制了四份文本然后做不同角度的便宜,模拟描边,要不就用自己实现的(挖坑待填);
  6. +
  7. 全屏UI界面打开时建议将场景3D相机移走或者关闭。
  8. +
  9. 如果非特殊需求没必要使用CanvasPixedPerfect,因为比如在scrollView时,滑动视图时一直会导致不断重绘产生性能损耗;
  10. +
  11. 不需要接受点击时间的物件将RayCastTarget关掉减少事件响应,从底层看是因为UGUI的RayCastTarget的响应是从数组中遍历检测是否和用户的点击区域响应,所以能够缩减数组大小自然能够缩减遍历次数;
  12. +
  13. UI动静分离
  14. +
  15. 对于UI上常用的改变颜色的操作,不要使用Image组件上的Color属性改变颜色,这样会导致整个Canvas的重建,可以新建个材质,设置材质给Image的Material通过修改材质的颜色来达到同样效果
  16. +
  17. 对于界面上常用的组件隐藏,别使用SetActive来控制显隐,有两种方式,一种是通过设置物体上CanvasRendererCullTransparentMesh并且控制物体透明度进行剔除,这个一般用于单个ui,如果是多个UI要不显示的话通过设置CanvasGroupsAlpha控制显隐。
  18. +
  19. Text的组件的BestFit非必要别开,因为这样开启这个会不断生成各种尺寸的字号字体图集,增加不必要开销。
  20. +
  21. 生成图集
  22. +
  23. Image组件不要选择None,也会使用一个默认图片且无法与图集合批,选用一张图片来统一使用并打入到图集中。
  24. +
  25. 不要使用透明为0的图片当作按钮,改用NoOverdrawImage来代替
  26. +
+
//不渲染但可以相应点击
+public class NoOverdrawImage : Graphic{    public override void Rebuild(CanvasUpdate update){}   }   
+
+public class Empty4Raycast : MaskableGraphic    {
+        protected Empty4Raycast(){useLegacyMeshGeneration = false;}
+        protected override void OnPopulateMesh(VertexHelper toFill){ toFill.Clear();        }    }
+
+
    +
  1. UI相机看不到的物体也会被渲染,占用DC,可以先禁用它们
  2. +
+
+

参考: +https://www.jianshu.com/p/4aa2b8641875

+
+

UI问题分析

+
+

ui常见的问题: +1. GPU 片段着色器使用过多(即填充率过度使用) +1. 重建 Canvas 批处理所花费的 CPU 时间过多 +1. Canvas 批次的重建数量过多(过度污染) +1. 用于生成顶点的 CPU 时间过多(通常来自文本)

+

sprite 被意外引用,造成多加载一个图集 +

+
+

UGUI 优化记录

+

主界面优化

+

优化前批次 84

+

+ + + +

+
+

红点图标的图集和主面板的icon图集不是一个,所以所有红点图标不能合批

+
+
    +
  • 位置1
    +

    所有按钮使用的图集 uileitubiao_1

    +
    +
  • +
+

mask使用 用来处理动画 +

+

mask(没用的),没用用的节点动画 +

+

底图 + +这个底图独自占一个批次,没有使用sprite,不能合批

+

+

指定一个上层sprite所在图集的一个背景纹理即可减少一个批次,背景图会和上层的icon按钮合批。
+内层mask没有影响合批,后面再测试

+
    +
  • 位置2 +邮件图标和位置1的图标时同一个图集
  • +
+

位置1 的内层mask和外层mask都移除后,会和邮件按钮进行合批

+
    +
  • 位置3 +活动图标的图集和位置1位置2的图集不同
  • +
+

活动图标的图集改为位置1用的图集

+
    +
  • 位置4,5,10
  • +
+

位置4 的icon使用的图集 common_banzi,feitongyong_01,uileitubiao_1 +位置5 的icon使用的图集 common_banziuileitubiao_1 +位置10 的icon使用的图集 common_banzi,uileitubiao_3,feitongyong_03

+
    +
  • 位置6
    +

    单独测试占用12批次

    +
    +
  • +
+

icon使用的图集 common_banziuileitubiao_4 +特效节点 effect_task_huang
+特效没有合批 +有粒子特效(Particle System),需要使用替代方案

+

红点有canvas,造成红点底图还有红点文字合批失败,这个canvas要控制红点在特效前面 +红点文字的字体和其他文字字体不一致,打断合批

+

无用的空Image节点占用一个批次,且打断合批 (这里Image透明度为0) +

+
+

优化后独立测试占用5批次,特效应该还有优化空间

+
+
    +
  • 位置7
    +

    批次 6

    +
    +
  • +
+

icon使用的图集 uileitubiao_1 + + +不能合批是因为左侧红点的mesh比较大(红点图标透明区域很大),被右侧图标遮挡了,就会插入到左侧图标和右侧图标中间

+
+

优化后3

+
+
    +
  • +

    位置8 +icon使用的图集 uileitubiao_1

    +
  • +
  • +

    位置9 +icon使用的图集 uileitubiao_1

    +
  • +
  • +

    位置20 +icon使用的图集 common_banziuileitubiao_4 +还有空Image 但是alpha不是0,不能合批

    +
  • +
  • +

    位置11

    +
    +

    批次 6

    +
    +
  • +
+

icon使用的图集 common_banzidaojutubiao_1

+
    +
  • +

    位置19 +icon使用的图集 common_banzi

    +
  • +
  • +

    位置 14,15,16,17,18 (活动集合) +icon使用的图集 uileitubiao_1 ,uileitubiao_4,feitongyong_11,
    +有粒子特效(Particle System),需要使用替代方案 +还有其他特效

    +
  • +
+

这里特效有两部分,一部分在ui底下,一部分在ui上面,美术同学做特效时给所在的prefab所有ui节点挂载Canvas组件,直接导致合批失败 且形成大量的drawcall

+
    +
  • 位置12
  • +
+

icon使用的图集 common_banzi
+有循环动画

+
    +
  • +

    位置13 +icon使用的图集 feitongyong_12

    +
  • +
  • +

    位置23,24

    +
  • +
+

icon使用的图集 uileitubiao_3,uileitubiao_4,uileitubiao_6 +有循环动画

+
    +
  • +

    位置21 +icon使用的图集 uileitubiao_1

    +
  • +
  • +

    位置22 +icon使用的图集 feitongyong_04,feitongyong_03

    +
  • +
  • +

    位置 搜索坐标, 收藏按钮 +icon使用的图集 feitongyong_05,feitongyong_01feitongyong_03

    +
  • +
+
优化后的结果:
+

+不显示特效的情况下是11个drawcall。 +4号位置占用3个drawcall,头像和框分别是独立的图集,体力条需要压在框的上面,有的框是特效做的,头像是允许玩家上传自定义头像的,所以不能合批也不能做到一个图集里,动态图集可以考虑但是不能处理特效框的情况。 +6号位置的红点有canvas,需要控制红点在特效前面 +12号位置占用2个drawcall,这里是animation做的动画,这里的底图和文字是不能合批的,否则每帧都构建合批的大mesh
+22号位置的头像和状态图标是在一个图集里,这里需要一个drawcall。
+剩下的主界面图片合批成1个drawcall,剩下的文字占用2个drawcall,因为是两种字体。

+

显示特效的情况下是31个drawcall +除去9个drawcall,都是特效占用的 +

+

全屏透明图片优化

+

原始的Overdraw +

+

优化后的Overdraw +

+

UIParticle性能对比

+
    +
  • 优化前总耗时40毫秒:
  • +
+

+

+
    +
  • UIParticle去掉后的耗时35毫秒(将particle继续显示通过3d的方式):
  • +
+

节省耗时5毫秒,一共移除UIParticle组件66个,共33个prefab。

+

+
    +
  • +

    游戏中实际情况优化前: +

    +
  • +
  • +

    游戏中实际情况禁用UIParticle后: +

    +
  • +
+

这里不能参考总耗时,因为游戏业务逻辑在运行,场景有业务在运行,只能参考单项,相对来说也不算准确

+

ui冗余相机

+

增加额外的2个drawcall +

+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git "a/ANote/Unity-URP\347\254\224\350\256\260/index.html" "b/ANote/Unity-URP\347\254\224\350\256\260/index.html" new file mode 100644 index 00000000..9aacd8da --- /dev/null +++ "b/ANote/Unity-URP\347\254\224\350\256\260/index.html" @@ -0,0 +1,9065 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Unity-URP笔记 - 王国庆的博客 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + 跳转至 + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ +
+ + + + + + + + + +

Unity-URP笔记

+

SRP/URP/HDRP之间的关系

+

+

SRP是什么?

+

SRP全称为Scriptable Render Pipeline(可编程渲染管线/脚本化渲染管线),是Unity提供的新渲染系统,可以在Unity通过C#脚本调用一系列API配置和执行渲染命令的方式来实现渲染流程,SRP将这些命令传递给Unity底层图形体系结构,然后再将指令发送给图形API。

+

说白了就是我们可以用SRP的API来创建自定义的渲染管线,可用来调整渲染流程或修改或增加功能。

+

它主要把渲染管线拆分成二层:

+
    +
  • +

    一层是比较底层的渲染API层,像OpenGL,D3D等相关的都封装起来。

    +
  • +
  • +

    另一层是渲染管线上层,上层代码使用C#来编写。在C#这层不需要关注底层在不同平台上渲染API的差别,也不需要关注具体如何做一个Draw Call

    +
  • +
+

URP是什么?

+

它的全称为Universal Render Pipeline(通用渲染管线),简称 Universal RP, 它是Unity官方基于SRP提供的模板,它的前身是 LWRP(Lightweight RP 即轻量级渲染管线), 在2019.3开始改名为URP,它涵盖了范围广泛的不同平台,是针对跨平台开发而构建的,性能比内置管线要好,另外可以进行自定义,实现不同风格的渲染,通用渲染管线未来将成为在Unity中进行渲染的基础 。

+

平台范围:可以在Unity当前支持的任何平台上使用

+

+

HDRP是什么?

+

它的全称为High Definition Render Pipeline(高清晰度渲染管线),它也是Unity官方基于SRP提供的模板,它更多是针对高端设备,如游戏主机和高端台式机,它更关注于真实感图形和渲染,该管线仅于以下平台兼容:

+
    +
  • Windows和Windows Store,带有DirectX 11或DirectX 12和Shader Model 5.0
  • +
  • 现代游戏机(Sony PS4和Microsoft Xbox One)
  • +
  • 使用金属图形的MacOS(最低版本10.13)
  • +
  • 具有Vulkan的Linux和Windows平台 +在此文章对HDRP不过多描述。
  • +
+

+

为什么诞生SRP?

+
    +
  • +

    内置渲染管线的缺陷

    +
      +
    • 定制性差:过去,Unity有一套内置渲染管线,渲染管线全部写在引擎的源码里。大家基本上不能改动,除非是买了Unity源码客户,当然大部分开发者是不会去改源码,所以过去的管线对开发者来说,很难进行定制。
    • +
    • 代码脓肿,效果效率无法做到最佳:内置渲染管线在一个渲染管线里面支持所有的二十多个平台,包括非常高端的PC平台,也包括非常低端的平台,很老的手机也要支持,所以代码越来越浓肿,很难做到使效率和效果做到最佳。
    • +
    • +

      目的

      +
    • +
    • +

      为了解决仅有一个默认渲染管线,造成的可配置型、可发现性、灵活性等问题。决定在C++端保留一个非常小的渲染内核,让C#端可以通过API暴露出更多的选择性,也就是说,Unity会提供一系列的C# API以及内置渲染管线的C#实现;这样一来,一方面可以保证C++端的代码都能严格通过各种白盒测试,另一方面C#端代码就可以在实际项目中调整。

      +
    • +
    +
  • +
+

+

+
+

渲染流水线图: +20210726144830

+

ref: https://blog.csdn.net/qq_30259857/article/details/108318528
+ref: https://blog.csdn.net/qq_33700123/article/details/114092028

+
+

SRPBatcher

+
+

详解参考官方文档: 可编程渲染管线 SRP Batcher

+
+

URP 面板详解

+
+

面板详解: 通用渲染管线(URP)_学习笔记
+20210726180522
+20210726180600

+
+

UniversalRenderPipeline 源码分析

+
+

UnityEngine.Rendering.Universal.UniversalRenderPipeline +public sealed partial class UniversalRenderPipeline : RenderPipeline +不包括XR,SceneView,Camera Preview,只是前向渲染的分析;不包括RenderingMode.Deferred的分析

+
+

Render分析

+
+

protected override void Render(ScriptableRenderContext renderContext, Camera[] cameras)的执行流程

+
+
    +
  • BeginFrameRendering(renderContext, cameras);
  • +
  • 配置GraphicsSettings
  • +
  • 设置Shader全局变量 SetupPerFrameShaderConstants();
  • +
  • SortCameras(cameras); // 根据camera.depth排序
  • +
  • RenderCameraStack(renderContext, camera);
      +
    • UpdateVolumeFramework(baseCamera, baseCameraAdditionalData);
    • +
    • BeginCameraRendering(renderContext, camera);
    • +
    • UpdateVolumeFramework(camera, null);
    • +
    • RenderSingleCamera(renderContext, camera);
    • +
    • EndCameraRendering(renderContext, camera);
    • +
    • EndFrameRendering(renderContext, cameras);
    • +
    +
  • +
+
        protected override void Render(ScriptableRenderContext renderContext, Camera[] cameras)
+        {
+            BeginFrameRendering(renderContext, cameras);
+            GraphicsSettings.lightsUseLinearIntensity = (QualitySettings.activeColorSpace == ColorSpace.Linear);
+            GraphicsSettings.useScriptableRenderPipelineBatching = asset.useSRPBatcher;
+            SetupPerFrameShaderConstants();
+            SortCameras(cameras);
+            for (int i = 0; i < cameras.Length; ++i){
+                var camera = cameras[i];
+                if (IsGameCamera(camera))
+                {
+                    RenderCameraStack(renderContext, camera);
+                }
+                else
+                {
+                    BeginCameraRendering(renderContext, camera);
+                    UpdateVolumeFramework(camera, null);
+                    RenderSingleCamera(renderContext, camera);
+                    EndCameraRendering(renderContext, camera);
+                }
+            }
+            EndFrameRendering(renderContext, cameras);
+        }
+        public static void RenderSingleCamera(ScriptableRenderContext context, Camera camera)
+        {
+            UniversalAdditionalCameraData additionalCameraData = null;
+            if (IsGameCamera(camera))
+                camera.gameObject.TryGetComponent(out additionalCameraData);
+            InitializeCameraData(camera, additionalCameraData, true, out var cameraData);
+            RenderSingleCamera(context, cameraData, cameraData.postProcessEnabled);
+        }    
+        /// <summary>
+        /// Renders a single camera. This method will do culling, setup and execution of the renderer.
+        /// </summary>
+        /// <param name="context">Render context used to record commands during execution.</param>
+        /// <param name="cameraData">Camera rendering data. This might contain data inherited from a base camera.</param>
+        /// <param name="anyPostProcessingEnabled">True if at least one camera has post-processing enabled in the stack, false otherwise.</param>
+        static void RenderSingleCamera(ScriptableRenderContext context, CameraData cameraData, bool anyPostProcessingEnabled)
+        {
+            Camera camera = cameraData.camera;
+            var renderer = cameraData.renderer;
+            if (!TryGetCullingParameters(cameraData, out var cullingParameters))
+                return;
+            ScriptableRenderer.current = renderer;
+            bool isSceneViewCamera = cameraData.isSceneViewCamera;
+            CommandBuffer cmd = CommandBufferPool.Get();
+                renderer.Clear(cameraData.renderType);
+                renderer.SetupCullingParameters(ref cullingParameters, ref cameraData);
+                context.ExecuteCommandBuffer(cmd); // Send all the commands enqueued so far in the CommandBuffer cmd, to the ScriptableRenderContext context
+                cmd.Clear();
+                var cullResults = context.Cull(ref cullingParameters);
+                InitializeRenderingData(asset, ref cameraData, ref cullResults, anyPostProcessingEnabled, out var renderingData);
+                renderer.Setup(context, ref renderingData);
+                renderer.Execute(context, ref renderingData);
+            context.ExecuteCommandBuffer(cmd); // Sends to ScriptableRenderContext all the commands enqueued since cmd.Clear, i.e the "EndSample" command
+            CommandBufferPool.Release(cmd);
+                context.Submit(); // Actually execute the commands that we previously sent to the ScriptableRenderContext context
+        }     
+
+        /// <summary>
+        // Renders a camera stack. This method calls RenderSingleCamera for each valid camera in the stack.
+        // The last camera resolves the final target to screen.
+        /// </summary>
+        /// <param name="context">Render context used to record commands during execution.</param>
+        /// <param name="camera">Camera to render.</param>
+        static void RenderCameraStack(ScriptableRenderContext context, Camera baseCamera)
+        {
+            baseCamera.TryGetComponent<UniversalAdditionalCameraData>(out var baseCameraAdditionalData);
+            // Overlay cameras will be rendered stacked while rendering base cameras
+            if (baseCameraAdditionalData != null && baseCameraAdditionalData.renderType == CameraRenderType.Overlay)
+                return;
+            // renderer contains a stack if it has additional data and the renderer supports stacking
+            var renderer = baseCameraAdditionalData?.scriptableRenderer;
+            bool supportsCameraStacking = renderer != null && renderer.supportedRenderingFeatures.cameraStacking;
+            List<Camera> cameraStack = (supportsCameraStacking) ? baseCameraAdditionalData?.cameraStack : null;
+            bool anyPostProcessingEnabled = baseCameraAdditionalData != null && baseCameraAdditionalData.renderPostProcessing;
+            // We need to know the last active camera in the stack to be able to resolve
+            // rendering to screen when rendering it. The last camera in the stack is not
+            // necessarily the last active one as it users might disable it.
+            int lastActiveOverlayCameraIndex = -1;
+            if (cameraStack != null)
+            {
+                var baseCameraRendererType = baseCameraAdditionalData?.scriptableRenderer.GetType();
+                baseCameraAdditionalData.UpdateCameraStack();
+            }
+            // Post-processing not supported in GLES2.
+            anyPostProcessingEnabled &= SystemInfo.graphicsDeviceType != GraphicsDeviceType.OpenGLES2;
+            bool isStackedRendering = lastActiveOverlayCameraIndex != -1;
+            // Update volumeframework before initializing additional camera data
+            UpdateVolumeFramework(baseCamera, baseCameraAdditionalData);
+            InitializeCameraData(baseCamera, baseCameraAdditionalData, !isStackedRendering, out var baseCameraData);
+            BeginCameraRendering(context, baseCamera);
+            RenderSingleCamera(context, baseCameraData, anyPostProcessingEnabled);
+            EndCameraRendering(context, baseCamera);
+            if (isStackedRendering)
+            {
+                for (int i = 0; i < cameraStack.Count; ++i)
+                {
+                    var currCamera = cameraStack[i];
+                    if (!currCamera.isActiveAndEnabled)
+                        continue;
+                    currCamera.TryGetComponent<UniversalAdditionalCameraData>(out var currCameraData);
+                    // Camera is overlay and enabled
+                    // Copy base settings from base camera data and initialize initialize remaining specific settings for this camera type.
+                    CameraData overlayCameraData = baseCameraData;
+                    bool lastCamera = i == lastActiveOverlayCameraIndex;
+                    BeginCameraRendering(context, currCamera);
+                    UpdateVolumeFramework(currCamera, currCameraData);
+                    InitializeAdditionalCameraData(currCamera, currCameraData, lastCamera, ref overlayCameraData);
+                    RenderSingleCamera(context, overlayCameraData, anyPostProcessingEnabled);
+                    EndCameraRendering(context, currCamera);
+                }
+            }
+        }                  
+
+
+

如果Unity版本高UNITY_2021_1_OR_NEWER宏生效则BeginContextRendering(renderContext, cameras);替换BeginFrameRendering(renderContext, cameras);EndContextRendering(renderContext, cameras);替换EndFrameRendering(renderContext, cameras);

+
+

RenderSingleCamera 分析

+
+

static void RenderSingleCamera(ScriptableRenderContext context, CameraData cameraData, bool anyPostProcessingEnabled)的执行流程
+ScriptableRender renderer= cameraData.renderer;

+
+
    +
  • +

    剔除操作,返回一个剔除参数(被摄象机渲染的游戏物体和光照的列表 )TryGetCullingParameters(cameraData, out var cullingParameters) + > static bool TryGetCullingParameters(CameraData cameraData, out ScriptableCullingParameters cullingParams)

    +
      +
    • cameraData.camera.TryGetCullingParameters(false, out cullingParams)
    • +
    • 修改剔除参数(是否投射阴影,最大灯光数量,阴影距离shadowDistance,)renderer.SetupCullingParameters(ref cullingParameters, ref cameraData);
      +

      public virtual void SetupCullingParameters(ref ScriptableCullingParameters cullingParameters, + ref CameraData cameraData)

      +
      +
    • +
    • context.ExecuteCommandBuffer(cmd);
      +

      public void ExecuteCommandBuffer(CommandBuffer commandBuffer);

      +
      +
    • +
    • 根据剔除参数执行剔除,获得剔除结果 var cullResults = context.Cull(ref cullingParameters);
      +

      public CullingResults Cull(ref ScriptableCullingParameters parameters)

      +
      +
    • +
    • 初始化渲染数据RenderingData(灯光数据,阴影数据,后处理数据,是否动态批处理,是否启用后处理,相机数据,剔除结果数据)InitializeRenderingData(asset, ref cameraData, ref cullResults, anyPostProcessingEnabled, out var renderingData);
      +

      static void InitializeRenderingData(UniversalRenderPipelineAsset settings, ref CameraData cameraData, ref CullingResults cullResults, + bool anyPostProcessingEnabled, out RenderingData renderingData)

      +
      +
    • +
    +
  • +
  • +

    renderer.Setup(context, ref renderingData) + > public abstract void Setup(ScriptableRenderContext context, ref RenderingData renderingData);

    +
      +
    • +

      如果获取深度 + ```csharp + ConfigureCameraTarget(BuiltinRenderTextureType.CameraTarget, BuiltinRenderTextureType.CameraTarget); + AddRenderPasses(ref renderingData); + EnqueuePass(m_RenderOpaqueForwardPass);

      +

      // TODO: Do we need to inject transparents and skybox when rendering depth only camera? They don't write to depth. +EnqueuePass(m_DrawSkyboxPass);
      +EnqueuePass(m_RenderTransparentForwardPass); +`` + -ConfigureCameraColorTarget(activeColorRenderTargetId);-AddRenderPasses(ref renderingData); // rendererFeatures AddRenderPasses-CreateCameraRenderTarget(context, ref cameraTargetDescriptor, createColorTexture, createDepthTexture); // CameraRenderType.Base-EnqueuePass(m_MainLightShadowCasterPass);-EnqueuePass(m_AdditionalLightsShadowCasterPass);-EnqueuePass(m_DepthNormalPrepass);或者EnqueuePass(m_DepthPrepass);-EnqueuePass(m_ColorGradingLutPass);-EnqueuePass(m_RenderOpaqueForwardPass);-EnqueuePass(m_DrawSkyboxPass);-EnqueuePass(m_CopyDepthPass);-EnqueuePass(m_CopyColorPass);-EnqueuePass(m_TransparentSettingsPass);-EnqueuePass(m_RenderTransparentForwardPass);-EnqueuePass(m_OnRenderObjectCallbackPass);-EnqueuePass(m_PostProcessPass);-EnqueuePass(m_FinalPostProcessPass);-EnqueuePass(m_CapturePass);-EnqueuePass(m_FinalBlitPass);-EnqueuePass(m_PostProcessPass);`

      +
    • +
    +
  • +
  • +

    renderer.Execute(context, ref renderingData); + > public void Execute(ScriptableRenderContext context, ref RenderingData renderingData) + > ref CameraData cameraData = ref renderingData.cameraData;

    +
      +
    • InternalStartRendering(context, ref renderingData);
    • +
    • SetPerCameraShaderVariables(cmd, ref cameraData);
    • +
    • SetShaderTimeValues(cmd, time, deltaTime, smoothDeltaTime);
    • +
    • context.ExecuteCommandBuffer(cmd);
    • +
    • SortStable(m_ActiveRenderPassQueue); // Sort the render pass queue
    • +
    • SetupLights(context, ref renderingData);
    • +
    • +

      执行所有Pass的Execute方法 ExecuteBlock(RenderPassBlock.BeforeRendering, in renderBlocks, context, ref renderingData); + >// Before Render Block. This render blocks always execute in mono rendering.
      + >// Camera is not setup. Lights are not setup.
      + >// Used to render input textures like shadowmaps. + csharp + void ExecuteBlock(int blockIndex, in RenderBlocks renderBlocks,ScriptableRenderContext context, ref RenderingData renderingData, bool submit = false) + { foreach (int currIndex in renderBlocks.GetRange(blockIndex)) //循环所有Pass + {var renderPass = m_ActiveRenderPassQueue[currIndex]; + ExecuteRenderPass(context, renderPass, ref renderingData);} + if (submit) context.Submit();} + void ExecuteRenderPass(ScriptableRenderContext context, ScriptableRenderPass renderPass, ref RenderingData renderingData){ + using var profScope = new ProfilingScope(null, renderPass.profilingSampler); + ref CameraData cameraData = ref renderingData.cameraData; + CommandBuffer cmd = CommandBufferPool.Get(); + // Track CPU only as GPU markers for this scope were "too noisy". + using (new ProfilingScope(cmd, Profiling.RenderPass.configure)) + { renderPass.Configure(cmd, cameraData.cameraTargetDescriptor); //配置Pass数据 + SetRenderPassAttachments(cmd, renderPass, ref cameraData);} + // Also, we execute the commands recorded at this point to ensure SetRenderTarget is called before RenderPass.Execute + context.ExecuteCommandBuffer(cmd); + CommandBufferPool.Release(cmd); + renderPass.Execute(context, ref renderingData); //执行对应Pass的Execute + }

      +
    • +
    • +

      context.SetupCameraProperties(camera); + >// This is still required because of the following reasons: + >// - Camera billboard properties. + >// - Camera frustum planes: unity_CameraWorldClipPlanes[6] + >// - _ProjectionParams.x logic is deep inside GfxDevice + >// NOTE: The only reason we have to call this here and not at the beginning (before shadows) + >// is because this need to be called for each eye in multi pass VR. + >// The side effect is that this will override some shader properties we already setup and we will have to + >// reset them.

      +
    • +
    • +

      SetCameraMatrices(cmd, ref cameraData, true);

      +
    • +
    • context.ExecuteCommandBuffer(cmd);
    • +
    • context.DrawWireOverlay(camera);
    • +
    • InternalFinishRendering(context, cameraData.resolveFinalTarget); + > context.ExecuteCommandBuffer(cmd);
    • +
    • context.ExecuteCommandBuffer(cmd);
    • +
    • context.ExecuteCommandBuffer(cmd);
    • +
    • context.Submit();
    • +
    +
  • +
+

URP笔记

+

URP LightModeTags 说明

+
+

Tags{“LightMode” = “XXX”}

+
+
    +
  • UniversalForward:前向渲染物件之用,SRPDefaultUnlit也用该pass渲染
  • +
  • ShadowCaster: 投射阴影之用
  • +
  • DepthOnly:只用来产生深度图
  • +
  • Mata:来用烘焙光照图之用
  • +
  • Universal2D :做2D游戏用的,用来替代前向渲染
  • +
  • UniversalGBuffer : 貌似与延迟渲染相关(开发中), Does GI + emission. All additional lights are done deferred as well as fog
  • +
  • UniversalForwardOnly:
  • +
  • NormalsRendering:
  • +
  • SceneSelectionPass:Scene view outline pass.
  • +
  • Picking:Scene picking buffer pass
  • +
  • DepthNormals: 只是用俩产生法线贴图_CameraNormalsTexture
  • +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git "a/ANote/Unity\350\256\260\345\275\225/index.html" "b/ANote/Unity\350\256\260\345\275\225/index.html" new file mode 100644 index 00000000..46025bdf --- /dev/null +++ "b/ANote/Unity\350\256\260\345\275\225/index.html" @@ -0,0 +1,8400 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Unity记录 - 王国庆的博客 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + 跳转至 + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+ +
+ + + + + + + + + +

Unity记录

+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git "a/ANote/caddy\347\254\224\350\256\260/index.html" "b/ANote/caddy\347\254\224\350\256\260/index.html" new file mode 100644 index 00000000..bf7283f7 --- /dev/null +++ "b/ANote/caddy\347\254\224\350\256\260/index.html" @@ -0,0 +1,8498 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + caddy笔记 - 王国庆的博客 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + 跳转至 + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+ +
+ + + + + + + + + +

caddy笔记

+

安装

+

CentOS7

+
yum install yum-plugin-copr
+yum copr enable @caddy/caddy
+yum install caddy
+
+

win or mac
+https://caddyserver.com/download

+

+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git "a/ANote/cloudflare\345\217\215\345\220\221\344\273\243\347\220\206\345\267\245\345\205\267/index.html" "b/ANote/cloudflare\345\217\215\345\220\221\344\273\243\347\220\206\345\267\245\345\205\267/index.html" new file mode 100644 index 00000000..54c3ac03 --- /dev/null +++ "b/ANote/cloudflare\345\217\215\345\220\221\344\273\243\347\220\206\345\267\245\345\205\267/index.html" @@ -0,0 +1,9164 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + cloudflare反向代理工具 - 王国庆的博客 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + 跳转至 + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ +
+ + + + + + + + + +

cloudflare反向代理工具

+

😀

+

当我们建立Cloudflare团队账户后,就打通了外网访问路径,实现了免费科学上网,但您是不是发现仍然不能正常访问ChatGPT,一会能访问一会又不能了,而且网速还很慢。今天通过对workers脚本的部署,手把手带您创建一个能无障碍访问油管、奈菲,chatgpt的科学上网环境。如果您还缺一个免费科学上网的备选方案,并且想了解worker脚本的部署方法,一定不要错过。

+

Video preview

+

📝 通过Workers脚本部署设置,手把手带您实现免费高速科学上网,无障碍访问奈菲,ChatGPT

+

脚本部署工具连接

+

v2rayN客户端及workers代码都托管在GitHub上,访问时需要科学上网。

+

1、v2rayN下载

+

2、登陆Cloudflare:

+

Cloudflare账户注册小布出过一期Zero Trust团队账户设置视频,其中演示了怎么注册Cloudflare账户,不会注册的朋友可以参考那期视频。

+

3、部署workers脚本:

+

Wordkers脚本:

+

部署中修改两个地方:替换UUID,CF反代IP,反代IP可以使用下面大佬提供的,也可以自己去筛选。如果使用的是[ 3K大佬的 ]worker脚本,替换下伪装域名。

+

http \= [80, 8080, 8880, 2052, 2086, 2095, 2082] https \= [443, 8443, 2053, 2096, 2087, 2083]

+

大佬提供的CF反代IP:

+

+
+

优选域名:

+
+

4、套Tls设置v2rayN(建议套上Tls)

+

套Tls需要将域名托管到Cloudflare,所以配置之前要想将域名托管设置好

+

4.1、添加自定义域

+

4.2、获取vless订阅地址

+

4.3、v2rayN中添加vless订阅地址

+

4.4、修改端口为443,传输层安全(Tls)开启

+

Workers脚本设置步骤

+

1、创建workers应用

+

notion image

+

notion image

+

notion image

+

2、添加workers脚本

+

2.1、复制github上的workers脚本

+

notion image

+

2.2、删除默认脚本并粘贴workers脚本

+

notion image

+

3、修改UserID和proxyIP

+

UserID可以通过在线生成UUID得到,也可通过v2rayN创建Vless连接中的生成用户ID得到。

+

proxyIP可以复制上面大佬提供的反代域名,也可以自己筛选得到

+

notion image

+

notion image

+

4、生成应用

+

4.1、保存并部署部署

+

notion image

+

notion image

+

获取未加密的Vless订阅连接

+

1、 打开连接

+

1.1、点workers.dev (点击此链接前需要科学上网)

+

notion image

+

1.2、如果看到下面的代码说明部署成功

+

notion image

+

2、添加UserID

+

在连接后面添加UserID(就是workers代码中UserID),注意添加反斜线 “ / ”,然后回车

+

notion image

+

3、生成Vless订阅连接

+

得到未套Tls的Vless订阅连接

+

notion image

+

获取Tls加密的Vless订阅连接

+

操作前需确定域名已经成功托管到Cloudflare上

+

1、添加自定义域

+

1.1、点击刚刚添加的edge应用

+

notion image

+

1.2、点击【查看】,或者点击【触发器】

+

notion image

+

1.3、点击【添加自定义域】

+

notion image

+

1.4、输入一个二级域名,名称随便起。二级域名要填写完整(二级域名.temptemps.top)

+

然后点击下面的添加自定义域按钮

+

notion image

+

1.5、添加自定义域后续等待证书申请完成

+

notion image

+

看到有效即证书申请完成

+

notion image

+

2、 打开连接

+

2.1、点击刚刚生成的二级域名

+

notion image

+

2.2、看到下面代码说明部署成功

+

notion image

+

3、生成Vless订阅连接

+

3.1、在域名后添加UserID回车,得到以套Tls的vless订阅连接

+

notion image

+

v2rayN客户端设置

+

1、添加未套Tls的vless连接

+

1.1、复制未加密vless订阅连接,粘贴到v2rayN客户端

+

notion image

+

1.2、打开v3rayN客户端,从剪贴板导入批量URL或者直接按Ctrl+V

+

notion image

+

notion image

+

1.3、双击连接打开配置页面,因为是为加密的vless订阅,不能使用443端口,将443端口改为80,传输层安全(Tls):设置为空

+

总共有7个端口可选,可以任选其中一个: [80, 8080, 8880, 2052, 2086, 2095, 2082]

+

notion image

+

2、添加已套Tls的vless连接

+

2.1、复制生成的Vless订阅连接

+

notion image

+

2.2、粘贴到v2rayN中

+

notion image

+

Vless添加后不用修改设置,如果出现不能访问的情况,可将地址修改为优选ip或优选域名

+

Tls端口总共有6个端口可选,可以任选其中一个: [443, 8443, 2053, 2096, 2087, 2083]

+

notion image

+

CloudFlare在线优选IP网站:

+

本地IP优选网址: http://ip.flares.cloud/
+在线IP优选网址: https://stock.hostmonit.com/CloudFlareYes
+在线IP优选网址: https://monitor.gacjie.cn/page/cloudflare/ipv4.html
+在线IP优选网址: https://monitor.gacjie.cn/page/cloudflare/cname.html
+在线IP优选网址: https://api.uouin.com/cloudflare.html

+

cloudflare 优选域名

+
*.cloudflare.182682.xyz
+115155.xyz
+cdn.2020111.xyz
+cf.0sm.com
+cf.090227.xyz
+f3058171cad.002404.xyz
+cf.zhetengsha.eu.org
+8.889288.xyz
+cf.zerone-cdn.pp.ua
+cf.cdn.bingbook.cn
+achk.cloudflarest.link
+cfip.1323123.xyz
+cf.877771.xyz
+cnamefuckxxs.yuchen.icu
+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git "a/ANote/golang\347\254\224\350\256\260/index.html" "b/ANote/golang\347\254\224\350\256\260/index.html" new file mode 100644 index 00000000..88478f12 --- /dev/null +++ "b/ANote/golang\347\254\224\350\256\260/index.html" @@ -0,0 +1,8571 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + golang笔记 - 王国庆的博客 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + 跳转至 + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ +
+ + + + + + + + + +

golang笔记

+

安装

+

mac 安装

+
brew update
+brew install go
+
+
+

参考: centos 7 安装golang1.17

+
+

设置代理环境变量

+

设置代理环境变量,再拉去golang.org的时候就不需要墙了。注意GO1.13及之后支持direct的写法

+
go env -w GOPROXY=https://goproxy.cn,direct
+
+

问题

+

GO在Visual Studio Code初次运行时提示go: go.mod file not found in current directory or any parent directory; see 'go help modules'

+

新建项目后需要在项目根目录执行 + sh + go mod init {项目名}

+

#### [Go] 解决missing go.sum entry for module providing package

+

当在代码中使用了第三方库 ,但是go.mod中并没有跟着更新的时候

+

如果直接run或者build就会报这个错误

+

missing go.sum entry for module providing package

+

可以使用go mod tidy 来整理依赖

+

这个命令会:

+

删除不需要的依赖包

+

下载新的依赖包

+

更新go.sum

+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git "a/ANote/image/CSharp\347\254\224\350\256\260/2022-04-12-14-05-49.png" "b/ANote/image/CSharp\347\254\224\350\256\260/2022-04-12-14-05-49.png" new file mode 100644 index 00000000..221841df --- /dev/null +++ "b/ANote/image/CSharp\347\254\224\350\256\260/2022-04-12-14-05-49.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:30169b846bb15948168f7c774bcf2ea6ed7647bdb28fde9caf050ca82ff5b896 +size 72809 diff --git "a/ANote/image/CSharp\347\254\224\350\256\260/2022-04-12-14-06-17.png" "b/ANote/image/CSharp\347\254\224\350\256\260/2022-04-12-14-06-17.png" new file mode 100644 index 00000000..4cad1d86 --- /dev/null +++ "b/ANote/image/CSharp\347\254\224\350\256\260/2022-04-12-14-06-17.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e510f94657e558bdec04ab894f7557dc0697daf8d4c1805b7e8996d4ce658d82 +size 22712 diff --git "a/ANote/image/CSharp\347\254\224\350\256\260/2022-06-28-16-40-28.png" "b/ANote/image/CSharp\347\254\224\350\256\260/2022-06-28-16-40-28.png" new file mode 100644 index 00000000..3a92e04a --- /dev/null +++ "b/ANote/image/CSharp\347\254\224\350\256\260/2022-06-28-16-40-28.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f1b233bb6d864506f5420a03422230ec24008cca2101e2080b1ceb44e4d3d7a9 +size 24026 diff --git "a/ANote/image/CSharp\347\254\224\350\256\260/2022-06-30-17-57-07.png" "b/ANote/image/CSharp\347\254\224\350\256\260/2022-06-30-17-57-07.png" new file mode 100644 index 00000000..ece254c6 --- /dev/null +++ "b/ANote/image/CSharp\347\254\224\350\256\260/2022-06-30-17-57-07.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:32e00ebd75edc25daffec23979af761118dcb77fe2ab399ef15b04b59907495a +size 178144 diff --git "a/ANote/image/CSharp\347\254\224\350\256\260/2022-06-30-18-01-02.png" "b/ANote/image/CSharp\347\254\224\350\256\260/2022-06-30-18-01-02.png" new file mode 100644 index 00000000..cd0a5e9d --- /dev/null +++ "b/ANote/image/CSharp\347\254\224\350\256\260/2022-06-30-18-01-02.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:93383793bb11ae1290277e8c4f0e0f6e7201da381f7c1440e8be5ea89dccedfa +size 279997 diff --git "a/ANote/image/CSharp\347\254\224\350\256\260/2022-06-30-18-01-37.png" "b/ANote/image/CSharp\347\254\224\350\256\260/2022-06-30-18-01-37.png" new file mode 100644 index 00000000..7a79badf --- /dev/null +++ "b/ANote/image/CSharp\347\254\224\350\256\260/2022-06-30-18-01-37.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:336d3cb4248cbdac7a86619ede0b6ae95efbaa021db1ea532b68c1a3a4a50d64 +size 29401 diff --git "a/ANote/image/CSharp\347\254\224\350\256\260/2022-06-30-18-02-10.png" "b/ANote/image/CSharp\347\254\224\350\256\260/2022-06-30-18-02-10.png" new file mode 100644 index 00000000..6f3c44a6 --- /dev/null +++ "b/ANote/image/CSharp\347\254\224\350\256\260/2022-06-30-18-02-10.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:41958d97a6be5e2425242c905d7695ea0f04607a5ade4e001756365f51444546 +size 48918 diff --git "a/ANote/image/CSharp\347\254\224\350\256\260/2022-06-30-18-02-50.png" "b/ANote/image/CSharp\347\254\224\350\256\260/2022-06-30-18-02-50.png" new file mode 100644 index 00000000..f6b15ac2 --- /dev/null +++ "b/ANote/image/CSharp\347\254\224\350\256\260/2022-06-30-18-02-50.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8f3e245260041cfc0caa9893c4a71e65abc7403d0ca8584d0ff26a33acf324a4 +size 24120 diff --git "a/ANote/image/CSharp\347\254\224\350\256\260/2022-06-30-18-09-05.png" "b/ANote/image/CSharp\347\254\224\350\256\260/2022-06-30-18-09-05.png" new file mode 100644 index 00000000..3e41c764 --- /dev/null +++ "b/ANote/image/CSharp\347\254\224\350\256\260/2022-06-30-18-09-05.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6880ead2002560a7f186b077efdceb01a4dab37c3044cb043dc4c070326033f7 +size 27318 diff --git "a/ANote/image/CSharp\347\254\224\350\256\260/2022-06-30-21-04-42.png" "b/ANote/image/CSharp\347\254\224\350\256\260/2022-06-30-21-04-42.png" new file mode 100644 index 00000000..333f7a20 --- /dev/null +++ "b/ANote/image/CSharp\347\254\224\350\256\260/2022-06-30-21-04-42.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:95ddaf3cf0ec3957b9d5eacdbf8ed72e35aea30590038694e0b09ec5f7f366fe +size 52217 diff --git "a/ANote/image/CSharp\347\254\224\350\256\260/2022-06-30-21-13-09.png" "b/ANote/image/CSharp\347\254\224\350\256\260/2022-06-30-21-13-09.png" new file mode 100644 index 00000000..b8b5689a --- /dev/null +++ "b/ANote/image/CSharp\347\254\224\350\256\260/2022-06-30-21-13-09.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d80039db0e2518ca367273a2a22119b6929293d70f5ac4db921ed64b049121e8 +size 23056 diff --git "a/ANote/image/CSharp\347\254\224\350\256\260/2022-07-04-21-50-43.png" "b/ANote/image/CSharp\347\254\224\350\256\260/2022-07-04-21-50-43.png" new file mode 100644 index 00000000..6f1dd1cf --- /dev/null +++ "b/ANote/image/CSharp\347\254\224\350\256\260/2022-07-04-21-50-43.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7765e1c16b0c75871f9dd154454fa4e91ce7eb4da879bb06aa8c68a4e135e8c2 +size 8910 diff --git "a/ANote/image/DOTS\347\254\224\350\256\260/2023-08-11-18-20-47.png" "b/ANote/image/DOTS\347\254\224\350\256\260/2023-08-11-18-20-47.png" new file mode 100644 index 00000000..6eef6a8e --- /dev/null +++ "b/ANote/image/DOTS\347\254\224\350\256\260/2023-08-11-18-20-47.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:145321da69f94a00a2925d5b6e2eea44f1a52e23fe8dd82e5b0f3bf3a75f53dc +size 181304 diff --git "a/ANote/image/DOTS\347\254\224\350\256\260/2023-08-11-18-46-13.png" "b/ANote/image/DOTS\347\254\224\350\256\260/2023-08-11-18-46-13.png" new file mode 100644 index 00000000..54ed5b22 --- /dev/null +++ "b/ANote/image/DOTS\347\254\224\350\256\260/2023-08-11-18-46-13.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:44171b225b68f35b3348d9e2ba520ccd729e9ee14d17c588cd0f7c4d1e7656f0 +size 125400 diff --git "a/ANote/image/DOTS\347\254\224\350\256\260/2023-08-11-18-46-38.png" "b/ANote/image/DOTS\347\254\224\350\256\260/2023-08-11-18-46-38.png" new file mode 100644 index 00000000..795dd9d5 --- /dev/null +++ "b/ANote/image/DOTS\347\254\224\350\256\260/2023-08-11-18-46-38.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:07e7d9bdf49a4c20b0a3994fb8cc24b0e0b24039543e6f07c89a0e3163009e59 +size 56932 diff --git "a/ANote/image/DOTS\347\254\224\350\256\260/2023-08-11-18-52-56.png" "b/ANote/image/DOTS\347\254\224\350\256\260/2023-08-11-18-52-56.png" new file mode 100644 index 00000000..a7c18df7 --- /dev/null +++ "b/ANote/image/DOTS\347\254\224\350\256\260/2023-08-11-18-52-56.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:736701300ea362731ad6fd880f47bf2bd6d2f3cb72cb849f60b381111c95b2e5 +size 53136 diff --git "a/ANote/image/DOTS\347\254\224\350\256\260/2023-08-11-18-56-53.png" "b/ANote/image/DOTS\347\254\224\350\256\260/2023-08-11-18-56-53.png" new file mode 100644 index 00000000..5d430a27 --- /dev/null +++ "b/ANote/image/DOTS\347\254\224\350\256\260/2023-08-11-18-56-53.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bb09339d44563395c87324474c0b2b1ff8e25e7e039363fce281cdfdee6a33f1 +size 119709 diff --git "a/ANote/image/DOTS\347\254\224\350\256\260/2023-08-11-19-02-00.png" "b/ANote/image/DOTS\347\254\224\350\256\260/2023-08-11-19-02-00.png" new file mode 100644 index 00000000..abaec46f --- /dev/null +++ "b/ANote/image/DOTS\347\254\224\350\256\260/2023-08-11-19-02-00.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ef39282e536e4b6fdf3cd58c62f4b5d12169ec08b2ccf98d3f584136fa456ac0 +size 42533 diff --git "a/ANote/image/DOTS\347\254\224\350\256\260/2023-08-11-19-02-31.png" "b/ANote/image/DOTS\347\254\224\350\256\260/2023-08-11-19-02-31.png" new file mode 100644 index 00000000..0343122d --- /dev/null +++ "b/ANote/image/DOTS\347\254\224\350\256\260/2023-08-11-19-02-31.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:69c25b4e6f123b9aa34ec17ac899109d18580202c6fc3083a7a8d6355d45ac91 +size 129409 diff --git "a/ANote/image/DOTS\347\254\224\350\256\260/2023-08-14-20-54-53.png" "b/ANote/image/DOTS\347\254\224\350\256\260/2023-08-14-20-54-53.png" new file mode 100644 index 00000000..dcbb7fe3 --- /dev/null +++ "b/ANote/image/DOTS\347\254\224\350\256\260/2023-08-14-20-54-53.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2f375e388363db380ff4b4cd1888551e709951cc0e9690659a8fe90f275c2098 +size 394334 diff --git "a/ANote/image/DOTS\347\254\224\350\256\260/2023-08-14-20-55-08.png" "b/ANote/image/DOTS\347\254\224\350\256\260/2023-08-14-20-55-08.png" new file mode 100644 index 00000000..7fb24e67 --- /dev/null +++ "b/ANote/image/DOTS\347\254\224\350\256\260/2023-08-14-20-55-08.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ba12dc0c440d394b6134fa788ebc36431dc368277ef18d72dfb4a786ccaeb5fc +size 321994 diff --git "a/ANote/image/DOTS\347\254\224\350\256\260/2023-08-14-21-09-54.png" "b/ANote/image/DOTS\347\254\224\350\256\260/2023-08-14-21-09-54.png" new file mode 100644 index 00000000..d053e3ec --- /dev/null +++ "b/ANote/image/DOTS\347\254\224\350\256\260/2023-08-14-21-09-54.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a80f3082be04a8818a28b151649f73ad4a106561f64d1fb377a319d3ac4cf218 +size 83322 diff --git "a/ANote/image/DOTS\347\254\224\350\256\260/2023-08-14-21-12-51.png" "b/ANote/image/DOTS\347\254\224\350\256\260/2023-08-14-21-12-51.png" new file mode 100644 index 00000000..bc017a63 --- /dev/null +++ "b/ANote/image/DOTS\347\254\224\350\256\260/2023-08-14-21-12-51.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a2141cf5acf655c18b123308fee0ffb158d926538a9694b2c64738434e247930 +size 152256 diff --git "a/ANote/image/DOTS\347\254\224\350\256\260/2023-08-14-21-25-59.png" "b/ANote/image/DOTS\347\254\224\350\256\260/2023-08-14-21-25-59.png" new file mode 100644 index 00000000..bf91b2b9 --- /dev/null +++ "b/ANote/image/DOTS\347\254\224\350\256\260/2023-08-14-21-25-59.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7f1c6138539c0d72e87a781e54626a38d1de65a9beff935f347f27a48dfa0af6 +size 129762 diff --git "a/ANote/image/DrawCall\344\274\230\345\214\226\345\256\236\346\210\230\346\274\224\350\256\262\347\250\277/20231214_ppt.jpg" "b/ANote/image/DrawCall\344\274\230\345\214\226\345\256\236\346\210\230\346\274\224\350\256\262\347\250\277/20231214_ppt.jpg" new file mode 100644 index 00000000..a0205e52 --- /dev/null +++ "b/ANote/image/DrawCall\344\274\230\345\214\226\345\256\236\346\210\230\346\274\224\350\256\262\347\250\277/20231214_ppt.jpg" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:023387439d09a1a313a1e1a67243d1989731f8eab74ed174cb2634b6b768cd70 +size 1938430 diff --git "a/ANote/image/EOC\351\241\271\347\233\256\345\210\206\346\236\220\347\254\224\350\256\2601/2022-03-29-11-09-55.png" "b/ANote/image/EOC\351\241\271\347\233\256\345\210\206\346\236\220\347\254\224\350\256\2601/2022-03-29-11-09-55.png" new file mode 100644 index 00000000..69c1769b --- /dev/null +++ "b/ANote/image/EOC\351\241\271\347\233\256\345\210\206\346\236\220\347\254\224\350\256\2601/2022-03-29-11-09-55.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1cd0902bb01c63cc63426f6c462b0788f2decbc37006d93f9a4588d9bea68ccc +size 22206 diff --git "a/ANote/image/EOC\351\241\271\347\233\256\345\210\206\346\236\220\347\254\224\350\256\2601/2022-03-29-11-10-55.png" "b/ANote/image/EOC\351\241\271\347\233\256\345\210\206\346\236\220\347\254\224\350\256\2601/2022-03-29-11-10-55.png" new file mode 100644 index 00000000..01db8483 --- /dev/null +++ "b/ANote/image/EOC\351\241\271\347\233\256\345\210\206\346\236\220\347\254\224\350\256\2601/2022-03-29-11-10-55.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5527395d7d69eb1f3672cda00186799b5e87814160e806a6dce40495084a0f2b +size 20763 diff --git "a/ANote/image/EOC\351\241\271\347\233\256\345\210\206\346\236\220\347\254\224\350\256\2601/2022-03-29-11-11-38.png" "b/ANote/image/EOC\351\241\271\347\233\256\345\210\206\346\236\220\347\254\224\350\256\2601/2022-03-29-11-11-38.png" new file mode 100644 index 00000000..00432032 --- /dev/null +++ "b/ANote/image/EOC\351\241\271\347\233\256\345\210\206\346\236\220\347\254\224\350\256\2601/2022-03-29-11-11-38.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b6d74a9848c6e1500c7752874efdeac164a69eb45b38b201575b5debd136ebba +size 20475 diff --git "a/ANote/image/EOC\351\241\271\347\233\256\345\210\206\346\236\220\347\254\224\350\256\2601/2022-03-29-11-23-25.png" "b/ANote/image/EOC\351\241\271\347\233\256\345\210\206\346\236\220\347\254\224\350\256\2601/2022-03-29-11-23-25.png" new file mode 100644 index 00000000..184269ee --- /dev/null +++ "b/ANote/image/EOC\351\241\271\347\233\256\345\210\206\346\236\220\347\254\224\350\256\2601/2022-03-29-11-23-25.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cd4c87d007604e4aa2da23b1682d6a2e43eca0afef9fa93c242c4ce7b6a138f0 +size 27559 diff --git "a/ANote/image/EOC\351\241\271\347\233\256\345\210\206\346\236\220\347\254\224\350\256\2601/2022-03-29-11-34-28.png" "b/ANote/image/EOC\351\241\271\347\233\256\345\210\206\346\236\220\347\254\224\350\256\2601/2022-03-29-11-34-28.png" new file mode 100644 index 00000000..779e4785 --- /dev/null +++ "b/ANote/image/EOC\351\241\271\347\233\256\345\210\206\346\236\220\347\254\224\350\256\2601/2022-03-29-11-34-28.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1729791f3be125efb3ea69abbb8df6ad5159cafdff16d2e4e4f109405d86a38b +size 32482 diff --git "a/ANote/image/EOC\351\241\271\347\233\256\345\210\206\346\236\220\347\254\224\350\256\2601/2022-03-29-11-37-20.png" "b/ANote/image/EOC\351\241\271\347\233\256\345\210\206\346\236\220\347\254\224\350\256\2601/2022-03-29-11-37-20.png" new file mode 100644 index 00000000..536d7e57 --- /dev/null +++ "b/ANote/image/EOC\351\241\271\347\233\256\345\210\206\346\236\220\347\254\224\350\256\2601/2022-03-29-11-37-20.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9a89ef9571ed41bb6433b3c05b2297b46eedc6548bae1ca099a4a4a9c08f3832 +size 20214 diff --git "a/ANote/image/EOC\351\241\271\347\233\256\345\210\206\346\236\220\347\254\224\350\256\2601/2022-03-29-11-38-52.png" "b/ANote/image/EOC\351\241\271\347\233\256\345\210\206\346\236\220\347\254\224\350\256\2601/2022-03-29-11-38-52.png" new file mode 100644 index 00000000..a9221492 --- /dev/null +++ "b/ANote/image/EOC\351\241\271\347\233\256\345\210\206\346\236\220\347\254\224\350\256\2601/2022-03-29-11-38-52.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b1e609b3055466cccf496e8a7e9e8f87eb3243514d73d2f562a25c0a5d5337f2 +size 31096 diff --git "a/ANote/image/EOC\351\241\271\347\233\256\345\210\206\346\236\220\347\254\224\350\256\2601/2022-03-29-12-00-56.png" "b/ANote/image/EOC\351\241\271\347\233\256\345\210\206\346\236\220\347\254\224\350\256\2601/2022-03-29-12-00-56.png" new file mode 100644 index 00000000..cd11607c --- /dev/null +++ "b/ANote/image/EOC\351\241\271\347\233\256\345\210\206\346\236\220\347\254\224\350\256\2601/2022-03-29-12-00-56.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e01d78e6e832ce9679a69a0d8df2f4357f97cd12c535f41b7a831812ee202194 +size 48824 diff --git "a/ANote/image/EOC\351\241\271\347\233\256\345\210\206\346\236\220\347\254\224\350\256\2601/2022-03-29-12-03-44.png" "b/ANote/image/EOC\351\241\271\347\233\256\345\210\206\346\236\220\347\254\224\350\256\2601/2022-03-29-12-03-44.png" new file mode 100644 index 00000000..3fcb9fff --- /dev/null +++ "b/ANote/image/EOC\351\241\271\347\233\256\345\210\206\346\236\220\347\254\224\350\256\2601/2022-03-29-12-03-44.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0bcf8884a8e880b487f17df40fcbc0726e64b719a5b8ebabc4c211ca46d6c360 +size 67748 diff --git "a/ANote/image/EOC\351\241\271\347\233\256\345\210\206\346\236\220\347\254\224\350\256\2601/2022-03-29-12-08-10.png" "b/ANote/image/EOC\351\241\271\347\233\256\345\210\206\346\236\220\347\254\224\350\256\2601/2022-03-29-12-08-10.png" new file mode 100644 index 00000000..6c32d67f --- /dev/null +++ "b/ANote/image/EOC\351\241\271\347\233\256\345\210\206\346\236\220\347\254\224\350\256\2601/2022-03-29-12-08-10.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d1027d6a1b89cdc7bc07ea9f9a6149b2899dd194c872a84b1a33c0aa1e658829 +size 138684 diff --git "a/ANote/image/EOC\351\241\271\347\233\256\345\210\206\346\236\220\347\254\224\350\256\2601/2022-03-29-14-02-22.png" "b/ANote/image/EOC\351\241\271\347\233\256\345\210\206\346\236\220\347\254\224\350\256\2601/2022-03-29-14-02-22.png" new file mode 100644 index 00000000..5e050381 --- /dev/null +++ "b/ANote/image/EOC\351\241\271\347\233\256\345\210\206\346\236\220\347\254\224\350\256\2601/2022-03-29-14-02-22.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:65c791cca5865203826a1d0c648b3a3a89da0e62db74922b053fca622d7bdb98 +size 18347 diff --git "a/ANote/image/EOC\351\241\271\347\233\256\345\210\206\346\236\220\347\254\224\350\256\2601/2022-03-29-14-05-26.png" "b/ANote/image/EOC\351\241\271\347\233\256\345\210\206\346\236\220\347\254\224\350\256\2601/2022-03-29-14-05-26.png" new file mode 100644 index 00000000..6245073e --- /dev/null +++ "b/ANote/image/EOC\351\241\271\347\233\256\345\210\206\346\236\220\347\254\224\350\256\2601/2022-03-29-14-05-26.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:86ce04fd99b0bb0f456667ddab774994485cea07a6ddb9027be9d37346827da7 +size 156637 diff --git "a/ANote/image/EOC\351\241\271\347\233\256\345\210\206\346\236\220\347\254\224\350\256\2601/2022-03-29-14-07-12.png" "b/ANote/image/EOC\351\241\271\347\233\256\345\210\206\346\236\220\347\254\224\350\256\2601/2022-03-29-14-07-12.png" new file mode 100644 index 00000000..24a0dc75 --- /dev/null +++ "b/ANote/image/EOC\351\241\271\347\233\256\345\210\206\346\236\220\347\254\224\350\256\2601/2022-03-29-14-07-12.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6760d98caad65bae551f4550889cd9375e8c3254370d6196c064d25b06bf9f49 +size 20685 diff --git "a/ANote/image/EOC\351\241\271\347\233\256\345\210\206\346\236\220\347\254\224\350\256\2601/2022-03-29-14-08-18.png" "b/ANote/image/EOC\351\241\271\347\233\256\345\210\206\346\236\220\347\254\224\350\256\2601/2022-03-29-14-08-18.png" new file mode 100644 index 00000000..0789bb05 --- /dev/null +++ "b/ANote/image/EOC\351\241\271\347\233\256\345\210\206\346\236\220\347\254\224\350\256\2601/2022-03-29-14-08-18.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b98df43ce68dab12eb6bc758cb1d6d796fefea11d9bd6536945a87a3ec69d546 +size 65381 diff --git "a/ANote/image/EOC\351\241\271\347\233\256\345\210\206\346\236\220\347\254\224\350\256\2601/2022-03-29-14-09-10.png" "b/ANote/image/EOC\351\241\271\347\233\256\345\210\206\346\236\220\347\254\224\350\256\2601/2022-03-29-14-09-10.png" new file mode 100644 index 00000000..7c2aa18b --- /dev/null +++ "b/ANote/image/EOC\351\241\271\347\233\256\345\210\206\346\236\220\347\254\224\350\256\2601/2022-03-29-14-09-10.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b652b41cc6e92bdedf07255ec060a9813ac439aec757c4a85f87ba3f7b8161a8 +size 16928 diff --git "a/ANote/image/EOC\351\241\271\347\233\256\345\210\206\346\236\220\347\254\224\350\256\2601/2022-03-29-14-10-28.png" "b/ANote/image/EOC\351\241\271\347\233\256\345\210\206\346\236\220\347\254\224\350\256\2601/2022-03-29-14-10-28.png" new file mode 100644 index 00000000..7810ee33 --- /dev/null +++ "b/ANote/image/EOC\351\241\271\347\233\256\345\210\206\346\236\220\347\254\224\350\256\2601/2022-03-29-14-10-28.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ff00c9938fb5991145f940da1ec24e21c368ec714a63b42c6856056684c1dc39 +size 19982 diff --git "a/ANote/image/EOC\351\241\271\347\233\256\345\210\206\346\236\220\347\254\224\350\256\2601/2022-03-29-14-40-41.png" "b/ANote/image/EOC\351\241\271\347\233\256\345\210\206\346\236\220\347\254\224\350\256\2601/2022-03-29-14-40-41.png" new file mode 100644 index 00000000..5ea2945d --- /dev/null +++ "b/ANote/image/EOC\351\241\271\347\233\256\345\210\206\346\236\220\347\254\224\350\256\2601/2022-03-29-14-40-41.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1af4c3f1ce0d31e24f4815bac89ff91620e1c93d1a8f38adfc60b7b610589bcd +size 26641 diff --git "a/ANote/image/EOC\351\241\271\347\233\256\345\210\206\346\236\220\347\254\224\350\256\2601/2022-03-29-14-43-09.png" "b/ANote/image/EOC\351\241\271\347\233\256\345\210\206\346\236\220\347\254\224\350\256\2601/2022-03-29-14-43-09.png" new file mode 100644 index 00000000..0841708b --- /dev/null +++ "b/ANote/image/EOC\351\241\271\347\233\256\345\210\206\346\236\220\347\254\224\350\256\2601/2022-03-29-14-43-09.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4f1caf256f90145f77dcbffe3ee8b30c7b0513e48562bdf7ed6b168640ad2ef7 +size 30031 diff --git "a/ANote/image/EOC\351\241\271\347\233\256\345\210\206\346\236\220\347\254\224\350\256\2601/2022-03-29-14-58-41.png" "b/ANote/image/EOC\351\241\271\347\233\256\345\210\206\346\236\220\347\254\224\350\256\2601/2022-03-29-14-58-41.png" new file mode 100644 index 00000000..584b93d1 --- /dev/null +++ "b/ANote/image/EOC\351\241\271\347\233\256\345\210\206\346\236\220\347\254\224\350\256\2601/2022-03-29-14-58-41.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c6006d98d4e054e1982b39236d33f68d4afd675cd7e6a6431ed20dccf4a01a1c +size 25772 diff --git "a/ANote/image/EOC\351\241\271\347\233\256\345\210\206\346\236\220\347\254\224\350\256\2601/2022-03-29-15-11-14.png" "b/ANote/image/EOC\351\241\271\347\233\256\345\210\206\346\236\220\347\254\224\350\256\2601/2022-03-29-15-11-14.png" new file mode 100644 index 00000000..af82e884 --- /dev/null +++ "b/ANote/image/EOC\351\241\271\347\233\256\345\210\206\346\236\220\347\254\224\350\256\2601/2022-03-29-15-11-14.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:aa8ec9ab8695387e1fd3c967afd0562e7acd3f12a5d1e561a679575452f4d693 +size 22705 diff --git "a/ANote/image/EOC\351\241\271\347\233\256\345\210\206\346\236\220\347\254\224\350\256\2601/2022-03-29-15-12-38.png" "b/ANote/image/EOC\351\241\271\347\233\256\345\210\206\346\236\220\347\254\224\350\256\2601/2022-03-29-15-12-38.png" new file mode 100644 index 00000000..61fe6f92 --- /dev/null +++ "b/ANote/image/EOC\351\241\271\347\233\256\345\210\206\346\236\220\347\254\224\350\256\2601/2022-03-29-15-12-38.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:828fe8d6670bc03cb598b02b41fd0c2a05fb5ff5f379c09478141382608137ec +size 13314 diff --git "a/ANote/image/EOC\351\241\271\347\233\256\345\210\206\346\236\220\347\254\224\350\256\2601/2022-03-29-16-49-32.png" "b/ANote/image/EOC\351\241\271\347\233\256\345\210\206\346\236\220\347\254\224\350\256\2601/2022-03-29-16-49-32.png" new file mode 100644 index 00000000..064c4bee --- /dev/null +++ "b/ANote/image/EOC\351\241\271\347\233\256\345\210\206\346\236\220\347\254\224\350\256\2601/2022-03-29-16-49-32.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6722dcb0de8569d559da8fda5498a672a6ba94941e29781b137999791135d716 +size 20945 diff --git "a/ANote/image/EOC\351\241\271\347\233\256\345\210\206\346\236\220\347\254\224\350\256\2601/2022-03-29-16-49-51.png" "b/ANote/image/EOC\351\241\271\347\233\256\345\210\206\346\236\220\347\254\224\350\256\2601/2022-03-29-16-49-51.png" new file mode 100644 index 00000000..259aa962 --- /dev/null +++ "b/ANote/image/EOC\351\241\271\347\233\256\345\210\206\346\236\220\347\254\224\350\256\2601/2022-03-29-16-49-51.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:eb02012c0af8581d9b6c5394560f5bf80264ef45f98bc9404e4f94aefdeaf917 +size 22752 diff --git "a/ANote/image/EOC\351\241\271\347\233\256\345\210\206\346\236\220\347\254\224\350\256\2601/2022-04-14-16-11-38.png" "b/ANote/image/EOC\351\241\271\347\233\256\345\210\206\346\236\220\347\254\224\350\256\2601/2022-04-14-16-11-38.png" new file mode 100644 index 00000000..dc7757fc --- /dev/null +++ "b/ANote/image/EOC\351\241\271\347\233\256\345\210\206\346\236\220\347\254\224\350\256\2601/2022-04-14-16-11-38.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a7dd90684a4b716024c4ad42e4b3371ec1ecbd10801a4f3ce139830ca8d0b178 +size 42232 diff --git "a/ANote/image/EOC\351\241\271\347\233\256\345\210\206\346\236\220\347\254\224\350\256\2601/2022-04-15-16-35-28.png" "b/ANote/image/EOC\351\241\271\347\233\256\345\210\206\346\236\220\347\254\224\350\256\2601/2022-04-15-16-35-28.png" new file mode 100644 index 00000000..fb61ae8d --- /dev/null +++ "b/ANote/image/EOC\351\241\271\347\233\256\345\210\206\346\236\220\347\254\224\350\256\2601/2022-04-15-16-35-28.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:43772866d4b1ceb59bc153351124bff980cfe5907f21c3fc3c2e2a618186477b +size 26173 diff --git "a/ANote/image/EOC\351\241\271\347\233\256\345\210\206\346\236\220\347\254\224\350\256\2601/2022-04-20-15-26-15.png" "b/ANote/image/EOC\351\241\271\347\233\256\345\210\206\346\236\220\347\254\224\350\256\2601/2022-04-20-15-26-15.png" new file mode 100644 index 00000000..4f0f5622 --- /dev/null +++ "b/ANote/image/EOC\351\241\271\347\233\256\345\210\206\346\236\220\347\254\224\350\256\2601/2022-04-20-15-26-15.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:29ae23440a38810d5858ed9cbcc14810f9654cf6a4f95087c13adccc5bd6612e +size 40400 diff --git "a/ANote/image/EOC\351\241\271\347\233\256\347\254\224\350\256\260/2022-07-06-19-47-56.png" "b/ANote/image/EOC\351\241\271\347\233\256\347\254\224\350\256\260/2022-07-06-19-47-56.png" new file mode 100644 index 00000000..d895599f --- /dev/null +++ "b/ANote/image/EOC\351\241\271\347\233\256\347\254\224\350\256\260/2022-07-06-19-47-56.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b93fd5b2169ce5662df7d76fb328ac274f58dfed01c2329e3a81ff9bb30347d0 +size 56583 diff --git "a/ANote/image/EOC\351\241\271\347\233\256\347\254\224\350\256\260/2022-08-12-15-07-20.png" "b/ANote/image/EOC\351\241\271\347\233\256\347\254\224\350\256\260/2022-08-12-15-07-20.png" new file mode 100644 index 00000000..2938f5d1 --- /dev/null +++ "b/ANote/image/EOC\351\241\271\347\233\256\347\254\224\350\256\260/2022-08-12-15-07-20.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f0d5e250b64be436fa14898b2011788e22504390c6e9c1245982933e97772b11 +size 3984 diff --git "a/ANote/image/EOC\351\241\271\347\233\256\347\254\224\350\256\260/2022-08-12-15-12-08.png" "b/ANote/image/EOC\351\241\271\347\233\256\347\254\224\350\256\260/2022-08-12-15-12-08.png" new file mode 100644 index 00000000..6d15b6af --- /dev/null +++ "b/ANote/image/EOC\351\241\271\347\233\256\347\254\224\350\256\260/2022-08-12-15-12-08.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:655def78f0cdd072af7cad77d8341b9940e5040ac74c232fe6cd926ea1f91a47 +size 17523 diff --git "a/ANote/image/EOC\351\241\271\347\233\256\347\254\224\350\256\260/2022-08-12-15-16-32.png" "b/ANote/image/EOC\351\241\271\347\233\256\347\254\224\350\256\260/2022-08-12-15-16-32.png" new file mode 100644 index 00000000..1ee46956 --- /dev/null +++ "b/ANote/image/EOC\351\241\271\347\233\256\347\254\224\350\256\260/2022-08-12-15-16-32.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b26e3444496f993993b458150ddd4ec2dfb60b290a4e9905c9a0e90f09807809 +size 22101 diff --git "a/ANote/image/EOC\351\241\271\347\233\256\347\254\224\350\256\260/2022-08-12-15-16-50.png" "b/ANote/image/EOC\351\241\271\347\233\256\347\254\224\350\256\260/2022-08-12-15-16-50.png" new file mode 100644 index 00000000..c17273c1 --- /dev/null +++ "b/ANote/image/EOC\351\241\271\347\233\256\347\254\224\350\256\260/2022-08-12-15-16-50.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4de60c4d99eb7c397983c1ba441cae5b439025c90aac43b3f4484b37398a0835 +size 4878 diff --git "a/ANote/image/EOC\351\241\271\347\233\256\347\254\224\350\256\260/2022-08-12-19-05-48.png" "b/ANote/image/EOC\351\241\271\347\233\256\347\254\224\350\256\260/2022-08-12-19-05-48.png" new file mode 100644 index 00000000..36e775ba --- /dev/null +++ "b/ANote/image/EOC\351\241\271\347\233\256\347\254\224\350\256\260/2022-08-12-19-05-48.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:17f64f9e48c7b9bb3d238b00747b74a77e949be9b87169d9a9652421c4b4fc9a +size 56319 diff --git "a/ANote/image/EOC\351\241\271\347\233\256\347\254\224\350\256\260/2022-08-18-18-19-05.png" "b/ANote/image/EOC\351\241\271\347\233\256\347\254\224\350\256\260/2022-08-18-18-19-05.png" new file mode 100644 index 00000000..bbc24ac5 --- /dev/null +++ "b/ANote/image/EOC\351\241\271\347\233\256\347\254\224\350\256\260/2022-08-18-18-19-05.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:53a1eadbcd378f2907bf2c90ad8c87910248216f2d8ab5fe71fc8363175bcf11 +size 98319 diff --git "a/ANote/image/EOC\351\241\271\347\233\256\347\254\224\350\256\260/2022-08-26-16-19-52.png" "b/ANote/image/EOC\351\241\271\347\233\256\347\254\224\350\256\260/2022-08-26-16-19-52.png" new file mode 100644 index 00000000..08be0284 --- /dev/null +++ "b/ANote/image/EOC\351\241\271\347\233\256\347\254\224\350\256\260/2022-08-26-16-19-52.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:56dabac72135aea921c1bc2ceb41273efa3e325d94182723aa076e23eb74a693 +size 112313 diff --git "a/ANote/image/EOC\351\241\271\347\233\256\347\254\224\350\256\260/2022-08-26-16-20-02.png" "b/ANote/image/EOC\351\241\271\347\233\256\347\254\224\350\256\260/2022-08-26-16-20-02.png" new file mode 100644 index 00000000..59963215 --- /dev/null +++ "b/ANote/image/EOC\351\241\271\347\233\256\347\254\224\350\256\260/2022-08-26-16-20-02.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:52667e0180a1020dbfd367e3944ea34dcfb3cacf65ab924ef14c31bc33440d3a +size 86565 diff --git "a/ANote/image/EOC\351\241\271\347\233\256\347\254\224\350\256\260/2022-09-15-17-34-30.png" "b/ANote/image/EOC\351\241\271\347\233\256\347\254\224\350\256\260/2022-09-15-17-34-30.png" new file mode 100644 index 00000000..0105002a --- /dev/null +++ "b/ANote/image/EOC\351\241\271\347\233\256\347\254\224\350\256\260/2022-09-15-17-34-30.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:66a08a8fc3320a66800060bf12fce0d64eb89b792ef16be151d921cb7ef316e9 +size 174754 diff --git "a/ANote/image/Git\347\254\224\350\256\260/2022-01-17-21-47-17.png" "b/ANote/image/Git\347\254\224\350\256\260/2022-01-17-21-47-17.png" new file mode 100644 index 00000000..1556cb93 --- /dev/null +++ "b/ANote/image/Git\347\254\224\350\256\260/2022-01-17-21-47-17.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8aee50d387532ff4e560eb9ef7fe9c31e4a681a1d61420896d08fbaeff27b85d +size 20665 diff --git "a/ANote/image/IGG\345\274\200\345\217\221\347\254\224\350\256\260/2023-04-11-16-51-18.png" "b/ANote/image/IGG\345\274\200\345\217\221\347\254\224\350\256\260/2023-04-11-16-51-18.png" new file mode 100644 index 00000000..41bf665d --- /dev/null +++ "b/ANote/image/IGG\345\274\200\345\217\221\347\254\224\350\256\260/2023-04-11-16-51-18.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5f7d2f43c3afac3089f6b31dc30efb4b72480123d8673a4f95d213db8caf6549 +size 136753 diff --git "a/ANote/image/IGG\345\274\200\345\217\221\347\254\224\350\256\260/2023-04-12-21-57-40.png" "b/ANote/image/IGG\345\274\200\345\217\221\347\254\224\350\256\260/2023-04-12-21-57-40.png" new file mode 100644 index 00000000..c2801038 --- /dev/null +++ "b/ANote/image/IGG\345\274\200\345\217\221\347\254\224\350\256\260/2023-04-12-21-57-40.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d6929248ee69825b5def63f7ecab6ec82f2d654fa7aee6c0e1197b943eeb35af +size 124332 diff --git "a/ANote/image/IGG\345\274\200\345\217\221\347\254\224\350\256\260/2023-04-13-10-50-05.png" "b/ANote/image/IGG\345\274\200\345\217\221\347\254\224\350\256\260/2023-04-13-10-50-05.png" new file mode 100644 index 00000000..b5a3312c --- /dev/null +++ "b/ANote/image/IGG\345\274\200\345\217\221\347\254\224\350\256\260/2023-04-13-10-50-05.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5a360dd519c872f90d9c777e108bb71ad32ca391b290eb535a10890dcc88baa9 +size 86353 diff --git "a/ANote/image/IGG\345\274\200\345\217\221\347\254\224\350\256\260/2023-04-13-11-07-09.png" "b/ANote/image/IGG\345\274\200\345\217\221\347\254\224\350\256\260/2023-04-13-11-07-09.png" new file mode 100644 index 00000000..81273c06 --- /dev/null +++ "b/ANote/image/IGG\345\274\200\345\217\221\347\254\224\350\256\260/2023-04-13-11-07-09.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:243528f4cc2a028a62757ea23ee953797c596bb118bf3e5453cd66e52db5b1a0 +size 90531 diff --git "a/ANote/image/IGG\345\274\200\345\217\221\347\254\224\350\256\260/2023-04-13-11-11-06.png" "b/ANote/image/IGG\345\274\200\345\217\221\347\254\224\350\256\260/2023-04-13-11-11-06.png" new file mode 100644 index 00000000..ecce36d8 --- /dev/null +++ "b/ANote/image/IGG\345\274\200\345\217\221\347\254\224\350\256\260/2023-04-13-11-11-06.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1533198bd0ded3e0e34d3068e9ba05bf947847963eb9610d193333c88de92f68 +size 103438 diff --git "a/ANote/image/IGG\345\274\200\345\217\221\347\254\224\350\256\260/2023-04-13-14-55-45.png" "b/ANote/image/IGG\345\274\200\345\217\221\347\254\224\350\256\260/2023-04-13-14-55-45.png" new file mode 100644 index 00000000..d2f47f1c --- /dev/null +++ "b/ANote/image/IGG\345\274\200\345\217\221\347\254\224\350\256\260/2023-04-13-14-55-45.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2e002b5ce55911708e96a42e48875f7e0c285dc9f461cf6b13f289e2d2954513 +size 126304 diff --git "a/ANote/image/IGG\345\274\200\345\217\221\347\254\224\350\256\260/2023-04-23-15-37-32.png" "b/ANote/image/IGG\345\274\200\345\217\221\347\254\224\350\256\260/2023-04-23-15-37-32.png" new file mode 100644 index 00000000..3b14f2fa --- /dev/null +++ "b/ANote/image/IGG\345\274\200\345\217\221\347\254\224\350\256\260/2023-04-23-15-37-32.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9ae37d72e4a1803756ab69eb3a579bb3d38103c113329fdc6b573d9aec61235a +size 126151 diff --git "a/ANote/image/IGG\345\274\200\345\217\221\347\254\224\350\256\260/2023-04-23-15-44-13.png" "b/ANote/image/IGG\345\274\200\345\217\221\347\254\224\350\256\260/2023-04-23-15-44-13.png" new file mode 100644 index 00000000..87577acc --- /dev/null +++ "b/ANote/image/IGG\345\274\200\345\217\221\347\254\224\350\256\260/2023-04-23-15-44-13.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c3c0cc595dfb99be47039a9a21cd4f8cf8a91bb4a393358790e1210befe35a30 +size 19353 diff --git "a/ANote/image/IGG\345\274\200\345\217\221\347\254\224\350\256\260/2023-04-23-15-46-39.png" "b/ANote/image/IGG\345\274\200\345\217\221\347\254\224\350\256\260/2023-04-23-15-46-39.png" new file mode 100644 index 00000000..2c4c439c --- /dev/null +++ "b/ANote/image/IGG\345\274\200\345\217\221\347\254\224\350\256\260/2023-04-23-15-46-39.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:29b9628e2fe97306e7d8c66993a428d172695ef9db5e5d8731486151316ecdc4 +size 12871 diff --git "a/ANote/image/IGG\345\274\200\345\217\221\347\254\224\350\256\260/2023-04-23-15-52-57.png" "b/ANote/image/IGG\345\274\200\345\217\221\347\254\224\350\256\260/2023-04-23-15-52-57.png" new file mode 100644 index 00000000..219d0619 --- /dev/null +++ "b/ANote/image/IGG\345\274\200\345\217\221\347\254\224\350\256\260/2023-04-23-15-52-57.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5e99290555768cd33ddc62732c163153c13a7d7e6b0d0a29430012e93df770da +size 62727 diff --git "a/ANote/image/IGG\345\274\200\345\217\221\347\254\224\350\256\260/2023-04-23-15-55-03.png" "b/ANote/image/IGG\345\274\200\345\217\221\347\254\224\350\256\260/2023-04-23-15-55-03.png" new file mode 100644 index 00000000..a024b97e --- /dev/null +++ "b/ANote/image/IGG\345\274\200\345\217\221\347\254\224\350\256\260/2023-04-23-15-55-03.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:959b4b55f5091143d62eef37fc8de30710e07875b6f5987433c980e2c423cc7c +size 28199 diff --git "a/ANote/image/IGG\345\274\200\345\217\221\347\254\224\350\256\260/2023-04-23-15-56-41.png" "b/ANote/image/IGG\345\274\200\345\217\221\347\254\224\350\256\260/2023-04-23-15-56-41.png" new file mode 100644 index 00000000..9b27b008 --- /dev/null +++ "b/ANote/image/IGG\345\274\200\345\217\221\347\254\224\350\256\260/2023-04-23-15-56-41.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7f8d0d7fdfe2420030bc67d2580c1fa1de8a11fe4b2b2562e89b28c756d19d41 +size 21233 diff --git "a/ANote/image/IGG\345\274\200\345\217\221\347\254\224\350\256\260/2023-04-23-15-58-56.png" "b/ANote/image/IGG\345\274\200\345\217\221\347\254\224\350\256\260/2023-04-23-15-58-56.png" new file mode 100644 index 00000000..9d0d53e5 --- /dev/null +++ "b/ANote/image/IGG\345\274\200\345\217\221\347\254\224\350\256\260/2023-04-23-15-58-56.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1b696347d575dd5c04fe94010ef4a0c870fddf62fbc583ee16d9849cf9b700e8 +size 21551 diff --git "a/ANote/image/IGG\345\274\200\345\217\221\347\254\224\350\256\260/2023-04-23-16-00-10.png" "b/ANote/image/IGG\345\274\200\345\217\221\347\254\224\350\256\260/2023-04-23-16-00-10.png" new file mode 100644 index 00000000..bc1aa99e --- /dev/null +++ "b/ANote/image/IGG\345\274\200\345\217\221\347\254\224\350\256\260/2023-04-23-16-00-10.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:930b54cd115969da3ed33a931df67ce7c6c4af44be3b818d1660bec2f2117c06 +size 46559 diff --git "a/ANote/image/IGG\345\274\200\345\217\221\347\254\224\350\256\260/2023-04-23-16-00-42.png" "b/ANote/image/IGG\345\274\200\345\217\221\347\254\224\350\256\260/2023-04-23-16-00-42.png" new file mode 100644 index 00000000..d13b181b --- /dev/null +++ "b/ANote/image/IGG\345\274\200\345\217\221\347\254\224\350\256\260/2023-04-23-16-00-42.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:aa0d1bf394a4601c292312a9bb67c86d69c71f3fbe83ae168a4d2c0ee7e45da5 +size 13325 diff --git "a/ANote/image/IGG\345\274\200\345\217\221\347\254\224\350\256\260/2023-04-23-16-03-28.png" "b/ANote/image/IGG\345\274\200\345\217\221\347\254\224\350\256\260/2023-04-23-16-03-28.png" new file mode 100644 index 00000000..e7bf2de0 --- /dev/null +++ "b/ANote/image/IGG\345\274\200\345\217\221\347\254\224\350\256\260/2023-04-23-16-03-28.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0435d1bad92a544b5c75e70558798fdbdb6e0401f04873bc83a38f1fd088d4f4 +size 55679 diff --git "a/ANote/image/IGG\345\274\200\345\217\221\347\254\224\350\256\260/2023-04-23-16-04-44.png" "b/ANote/image/IGG\345\274\200\345\217\221\347\254\224\350\256\260/2023-04-23-16-04-44.png" new file mode 100644 index 00000000..c6cb48dd --- /dev/null +++ "b/ANote/image/IGG\345\274\200\345\217\221\347\254\224\350\256\260/2023-04-23-16-04-44.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:365f35c92dcda4dae3474da569c0718d91cee96591d1e87d8d7e40e33df38a44 +size 36411 diff --git "a/ANote/image/IGG\345\274\200\345\217\221\347\254\224\350\256\260/2023-04-23-16-05-56.png" "b/ANote/image/IGG\345\274\200\345\217\221\347\254\224\350\256\260/2023-04-23-16-05-56.png" new file mode 100644 index 00000000..33aa7226 --- /dev/null +++ "b/ANote/image/IGG\345\274\200\345\217\221\347\254\224\350\256\260/2023-04-23-16-05-56.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3587f11930ac0f95a371d81dc6b6ba6d661447245b666d8619c8960461697c10 +size 47031 diff --git "a/ANote/image/IGG\345\274\200\345\217\221\347\254\224\350\256\260/2023-04-23-16-10-48.png" "b/ANote/image/IGG\345\274\200\345\217\221\347\254\224\350\256\260/2023-04-23-16-10-48.png" new file mode 100644 index 00000000..61dbea3b --- /dev/null +++ "b/ANote/image/IGG\345\274\200\345\217\221\347\254\224\350\256\260/2023-04-23-16-10-48.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5149132227ac0311a87aa5f99db4c3367283234034849cdffe830b5ab37b7285 +size 80089 diff --git "a/ANote/image/IGG\345\274\200\345\217\221\347\254\224\350\256\260/2023-04-23-16-15-48.png" "b/ANote/image/IGG\345\274\200\345\217\221\347\254\224\350\256\260/2023-04-23-16-15-48.png" new file mode 100644 index 00000000..ee585762 --- /dev/null +++ "b/ANote/image/IGG\345\274\200\345\217\221\347\254\224\350\256\260/2023-04-23-16-15-48.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9852320fd166f5d04bdb109a839e179ba8cd5d4d952a61677f2aabe46d90ff3f +size 81664 diff --git "a/ANote/image/IGG\345\274\200\345\217\221\347\254\224\350\256\260/2023-04-23-16-18-00.png" "b/ANote/image/IGG\345\274\200\345\217\221\347\254\224\350\256\260/2023-04-23-16-18-00.png" new file mode 100644 index 00000000..87274408 --- /dev/null +++ "b/ANote/image/IGG\345\274\200\345\217\221\347\254\224\350\256\260/2023-04-23-16-18-00.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5949272940d27693afa30ce3174b63cc811d32b4e998069f6e3e48003cad0eb6 +size 13522 diff --git "a/ANote/image/IGG\345\274\200\345\217\221\347\254\224\350\256\260/2023-04-23-16-39-24.png" "b/ANote/image/IGG\345\274\200\345\217\221\347\254\224\350\256\260/2023-04-23-16-39-24.png" new file mode 100644 index 00000000..5b661ee3 --- /dev/null +++ "b/ANote/image/IGG\345\274\200\345\217\221\347\254\224\350\256\260/2023-04-23-16-39-24.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c4a6a1a46e560558c3c42590be6ddc5f91943e6886703d215ea9062cce08bf37 +size 44164 diff --git "a/ANote/image/IGG\345\274\200\345\217\221\347\254\224\350\256\260/2023-04-23-17-23-13.png" "b/ANote/image/IGG\345\274\200\345\217\221\347\254\224\350\256\260/2023-04-23-17-23-13.png" new file mode 100644 index 00000000..27ff79ba --- /dev/null +++ "b/ANote/image/IGG\345\274\200\345\217\221\347\254\224\350\256\260/2023-04-23-17-23-13.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:773bad964ae9d30c02aae33e84a8522a2efcf7a9d248e6b35a64f4cac39b4749 +size 66098 diff --git "a/ANote/image/IGG\345\274\200\345\217\221\347\254\224\350\256\260/2023-04-25-16-40-28.png" "b/ANote/image/IGG\345\274\200\345\217\221\347\254\224\350\256\260/2023-04-25-16-40-28.png" new file mode 100644 index 00000000..c4a8e518 --- /dev/null +++ "b/ANote/image/IGG\345\274\200\345\217\221\347\254\224\350\256\260/2023-04-25-16-40-28.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:725f090fbc01793908e379fa950aee9fb1a84559d7de73196e43414ef025b957 +size 77306 diff --git "a/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-04-16-40-44.png" "b/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-04-16-40-44.png" new file mode 100644 index 00000000..7f075668 --- /dev/null +++ "b/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-04-16-40-44.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3d6261b965b8da6e511a8f355b04f54cb0ceeb9013cb590964030d6677ce36a1 +size 22314 diff --git "a/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-04-17-47-09.png" "b/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-04-17-47-09.png" new file mode 100644 index 00000000..daf180f5 --- /dev/null +++ "b/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-04-17-47-09.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:00097cca05246102375d5fd4b24f57641ac6d45aa392c582959ccbc0cebe66c6 +size 391684 diff --git "a/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-09-18-27-55.png" "b/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-09-18-27-55.png" new file mode 100644 index 00000000..6ce68cd5 --- /dev/null +++ "b/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-09-18-27-55.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e5f163d11d4a0e639909dcae0bff7467484dc557e8f5a6327dccf7d504b84c2a +size 73865 diff --git "a/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-09-18-34-12.png" "b/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-09-18-34-12.png" new file mode 100644 index 00000000..33f413a1 --- /dev/null +++ "b/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-09-18-34-12.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f4438b55ef5dd3bf8625556f2871bcba39b1bce89e1591128b1282e81ac4dd31 +size 65957 diff --git "a/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-09-20-09-29.png" "b/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-09-20-09-29.png" new file mode 100644 index 00000000..24742fe2 --- /dev/null +++ "b/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-09-20-09-29.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:499406a4c4aff5c01f0f0850c7618703925205f4c9d670244d2d6bb554f109fe +size 24240 diff --git "a/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-09-21-49-16.png" "b/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-09-21-49-16.png" new file mode 100644 index 00000000..d7fb050a --- /dev/null +++ "b/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-09-21-49-16.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:076e3d334ff026eb01542c1f5fb3b99c969b0aa9a2ee3275cc13e6b1bf9cb696 +size 46120 diff --git "a/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-11-18-13-36.png" "b/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-11-18-13-36.png" new file mode 100644 index 00000000..c81512f9 --- /dev/null +++ "b/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-11-18-13-36.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c46d2c2f3a8af2891f09c1800703aac423b023a48f0bcb9961c40d5867639867 +size 608882 diff --git "a/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-14-10-44-26.png" "b/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-14-10-44-26.png" new file mode 100644 index 00000000..6b1a44fa --- /dev/null +++ "b/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-14-10-44-26.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7189da6a911ec44ad8de79296d41a8d6990fd6a7735f7938ab27b719f524e5fe +size 113391 diff --git "a/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-14-11-14-43.png" "b/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-14-11-14-43.png" new file mode 100644 index 00000000..ed9b102f --- /dev/null +++ "b/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-14-11-14-43.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:530b13eb3b2de90f59e907f323a7e714b966269b2f84383a2486bbbdc9d8ea8a +size 88791 diff --git "a/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-14-15-01-24.png" "b/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-14-15-01-24.png" new file mode 100644 index 00000000..9a0f3850 --- /dev/null +++ "b/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-14-15-01-24.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7f0d432b09c897ddd0f0e7be42bbe29a6c42b97b3fe9f544a60b5f4d468294d4 +size 73237 diff --git "a/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-14-15-10-05.png" "b/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-14-15-10-05.png" new file mode 100644 index 00000000..8a2270cd --- /dev/null +++ "b/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-14-15-10-05.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7726af047b97b5a438675499864d42c11905192e54f59df5e125140e8289a018 +size 47477 diff --git "a/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-14-15-23-55.png" "b/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-14-15-23-55.png" new file mode 100644 index 00000000..28d9de2c --- /dev/null +++ "b/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-14-15-23-55.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:34a9f188de1db7e6435b3590b746fc540ef537705941052930e26b57db2bc019 +size 67220 diff --git "a/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-14-15-29-22.png" "b/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-14-15-29-22.png" new file mode 100644 index 00000000..ecefcd7c --- /dev/null +++ "b/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-14-15-29-22.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:510210da406f4495ebfcfe9ea346ad0d37dfab0f9a72d110202dd50117c75cc5 +size 45831 diff --git "a/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-14-17-16-34.png" "b/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-14-17-16-34.png" new file mode 100644 index 00000000..614846f8 --- /dev/null +++ "b/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-14-17-16-34.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9e33caac8be2129164c33f8bd087e74816b7b9ed220453e5275ae2163c870399 +size 50615 diff --git "a/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-14-17-25-39.png" "b/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-14-17-25-39.png" new file mode 100644 index 00000000..d35567f4 --- /dev/null +++ "b/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-14-17-25-39.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e7f60460f337548ddeb1cca1f80cfc1cfc8d5c83bd7b4163fc0256e6a67960ea +size 3717 diff --git "a/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-14-17-35-44.png" "b/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-14-17-35-44.png" new file mode 100644 index 00000000..140e96ea --- /dev/null +++ "b/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-14-17-35-44.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:eb0252492714269c9cf385bfd9667dbc1a4a224deab61a963a280dfc486ed5d6 +size 31306 diff --git "a/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-14-17-45-46.png" "b/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-14-17-45-46.png" new file mode 100644 index 00000000..e80b0d28 --- /dev/null +++ "b/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-14-17-45-46.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9280b31657410f56b5e18253a57e83e539a915360c181011c4bfe9af9266f5bd +size 144851 diff --git "a/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-15-11-11-47.png" "b/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-15-11-11-47.png" new file mode 100644 index 00000000..bf6937fd --- /dev/null +++ "b/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-15-11-11-47.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e133d7917c3e3779dc5a6ac3ff8b818867465b0e9b07ea39f9585fc13baa51ff +size 95967 diff --git "a/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-15-15-44-35.png" "b/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-15-15-44-35.png" new file mode 100644 index 00000000..d68222d4 --- /dev/null +++ "b/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-15-15-44-35.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c34bd730b276e5cd8a1c68f2136b19a22c02f8fb888ab2a33cd1137918540508 +size 157278 diff --git "a/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-15-15-47-08.png" "b/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-15-15-47-08.png" new file mode 100644 index 00000000..134cda2f --- /dev/null +++ "b/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-15-15-47-08.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1147d2733971b0e97f85ec734fe10d30feec6a76d985093f910be2e87fa14c0f +size 19901 diff --git "a/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-15-16-16-24.png" "b/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-15-16-16-24.png" new file mode 100644 index 00000000..f4f292ae --- /dev/null +++ "b/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-15-16-16-24.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4207f58019d57fb45e104cc6d1436ffa430094818b30265699b5cf363c22d345 +size 98368 diff --git "a/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-15-16-26-21.png" "b/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-15-16-26-21.png" new file mode 100644 index 00000000..5c20ab38 --- /dev/null +++ "b/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-15-16-26-21.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2aeeef49452e96b4bc46e563692b71f73a0fdf5720e3d9f4d5267077677455ca +size 141413 diff --git "a/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-15-16-28-46.png" "b/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-15-16-28-46.png" new file mode 100644 index 00000000..6a36a7ab --- /dev/null +++ "b/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-15-16-28-46.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5c412058d9316813af4aeb9a256524eeb8fb210218b247494b5b22dd12fe29f1 +size 140514 diff --git "a/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-15-16-36-34.png" "b/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-15-16-36-34.png" new file mode 100644 index 00000000..f88c1479 --- /dev/null +++ "b/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-15-16-36-34.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:42bb7802a0433daebcc20d19c7aab2bed64c7a8cd2dd64c4add745f6da659c02 +size 40753 diff --git "a/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-16-14-31-11.png" "b/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-16-14-31-11.png" new file mode 100644 index 00000000..52bb2f0d --- /dev/null +++ "b/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-16-14-31-11.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ef8ad6a7b288b37c9baf70d57eb38eb8d74db0af040d1710a4984bc7152fca6e +size 7133 diff --git "a/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-16-14-31-38.png" "b/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-16-14-31-38.png" new file mode 100644 index 00000000..55fdfdfc --- /dev/null +++ "b/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-16-14-31-38.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:933281bc122458070d3de2520e479e3a58d083e5b8f253006c67bfcacf324b82 +size 36062 diff --git "a/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-16-15-10-53.png" "b/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-16-15-10-53.png" new file mode 100644 index 00000000..c2bf2da5 --- /dev/null +++ "b/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-16-15-10-53.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bebe0918654b8cf0a4fd18a7fa6d559ca60350a7ee2fdc4c0b87be392155a3b9 +size 78639 diff --git "a/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-16-16-49-15.png" "b/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-16-16-49-15.png" new file mode 100644 index 00000000..01cccefe --- /dev/null +++ "b/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-16-16-49-15.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ba3f3a46d79f938a3910c7101c70cd5a6807fa347c8aa9850907c1f01223cab6 +size 37416 diff --git "a/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-16-18-15-09.png" "b/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-16-18-15-09.png" new file mode 100644 index 00000000..e804ce36 --- /dev/null +++ "b/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-16-18-15-09.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a0735ab79c30e13c4ed4ef88eb2c8ae011eb8a6be1e263e14f359208dc714c13 +size 1038945 diff --git "a/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-16-18-17-12.png" "b/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-16-18-17-12.png" new file mode 100644 index 00000000..b8c1adc1 --- /dev/null +++ "b/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-16-18-17-12.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0454f216c5d06c25d36dd85f0c9494fdc59c33d8fcf85a50c62f547c1ed8a427 +size 1042589 diff --git "a/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-17-11-40-02.png" "b/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-17-11-40-02.png" new file mode 100644 index 00000000..e1bf3d3b --- /dev/null +++ "b/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-17-11-40-02.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:575ed5c54dd08bedad7009fc688b9b23575d30da4c4a911aed450e78d56a38af +size 437434 diff --git "a/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-17-11-44-26.png" "b/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-17-11-44-26.png" new file mode 100644 index 00000000..d10847ac --- /dev/null +++ "b/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-17-11-44-26.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:618a6914d4aef52de66076f7b6f6c9995c73dc3d88dc5b98f772c644ffc23ce9 +size 81345 diff --git "a/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-17-15-32-01.png" "b/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-17-15-32-01.png" new file mode 100644 index 00000000..9844e79d --- /dev/null +++ "b/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-17-15-32-01.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e53d754658ae86b9249dd603704dd437cac3f64520e301af80096ab721da34c6 +size 59309 diff --git "a/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-20-15-11-26.png" "b/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-20-15-11-26.png" new file mode 100644 index 00000000..2b0d8076 --- /dev/null +++ "b/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-20-15-11-26.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9bae78b42588300df1add2a7052bb65974c20a3255ea782e82df8726ac74e700 +size 124440 diff --git "a/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-20-15-19-18.png" "b/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-20-15-19-18.png" new file mode 100644 index 00000000..658aafc9 --- /dev/null +++ "b/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-20-15-19-18.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2bbdbeb30ab65d598a27086f50ce32927917e0e211857dd81c0f140182be3cd7 +size 102141 diff --git "a/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-20-15-47-03.png" "b/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-20-15-47-03.png" new file mode 100644 index 00000000..674a8ab6 --- /dev/null +++ "b/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-20-15-47-03.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8c1277d9d550050992fb0dc9f05755dc2a66272e92c0196fbb0009debc0684d8 +size 150841 diff --git "a/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-21-14-48-34.png" "b/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-21-14-48-34.png" new file mode 100644 index 00000000..7b908d4c --- /dev/null +++ "b/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-21-14-48-34.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1deb2bbbf0d18c00e9a1e3b385282cfda426a95b557d2e5c6b23eeae109b51bf +size 141703 diff --git "a/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-22-14-55-37.png" "b/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-22-14-55-37.png" new file mode 100644 index 00000000..c015883e --- /dev/null +++ "b/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-22-14-55-37.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0045ab7a4656efc33d6f8121220241da206f187e3702c0b2cf77b3aaca79d7ec +size 132109 diff --git "a/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-22-14-57-04.png" "b/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-22-14-57-04.png" new file mode 100644 index 00000000..51fd7e6e --- /dev/null +++ "b/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-22-14-57-04.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:30254187a52d8e41ab2dd1213d66598ac23722ac4ff679e2df3cd6f2cf5153cd +size 117489 diff --git "a/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-22-16-38-03.png" "b/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-22-16-38-03.png" new file mode 100644 index 00000000..07675fd7 --- /dev/null +++ "b/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-22-16-38-03.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d6612fe418604c6325e1e4383174d0580596c7aeeedce6d19f9d66d4606936c8 +size 214632 diff --git "a/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-22-17-32-27.png" "b/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-22-17-32-27.png" new file mode 100644 index 00000000..237434f9 --- /dev/null +++ "b/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-22-17-32-27.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:69cf47f590d591c50320c2622fbef57726106b8711a47bb21abd95ff0d70453a +size 561438 diff --git "a/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-23-11-45-40.png" "b/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-23-11-45-40.png" new file mode 100644 index 00000000..bede3546 --- /dev/null +++ "b/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-23-11-45-40.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c72234597b9651dd1dfbc62d1d76f58890dad7e47e8bf001c26993048a29b739 +size 606405 diff --git "a/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-23-11-56-27.png" "b/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-23-11-56-27.png" new file mode 100644 index 00000000..9b549646 --- /dev/null +++ "b/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-23-11-56-27.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8253df7e54f16e93e75d6d6b82cfcb742144a37f63e2a020b1322e746f4086a2 +size 47969 diff --git "a/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-27-18-34-34.png" "b/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-27-18-34-34.png" new file mode 100644 index 00000000..cfd62fc1 --- /dev/null +++ "b/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-27-18-34-34.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:070cc9c2d07e7efbca2b3aa80313bff3d0d25b0b8a621fb7491ce6d714ad32a7 +size 465643 diff --git "a/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-30-11-35-31.png" "b/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-30-11-35-31.png" new file mode 100644 index 00000000..df738ff3 --- /dev/null +++ "b/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-30-11-35-31.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:764442ac526fa358aa8114072c58c368a921ddaac9ff0bf6af2353f327dbd8c5 +size 935981 diff --git "a/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-30-11-52-47.png" "b/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-30-11-52-47.png" new file mode 100644 index 00000000..748d12aa --- /dev/null +++ "b/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-30-11-52-47.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a220615821eb8c001ce127c00013eda7ad044cb5aaafbe14c09ffcb468528d50 +size 89716 diff --git "a/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-30-20-52-10.png" "b/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-30-20-52-10.png" new file mode 100644 index 00000000..00110320 --- /dev/null +++ "b/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-30-20-52-10.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c18bfd68465b84cbd78c57e16db746eb992968eec06e98617e9f336892bbecd5 +size 58826 diff --git "a/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-30-20-52-36.png" "b/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-30-20-52-36.png" new file mode 100644 index 00000000..9defb612 --- /dev/null +++ "b/ANote/image/IGG\346\200\247\350\203\275\345\210\206\346\236\220\347\254\224\350\256\260/2023-03-30-20-52-36.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e1cb8081b81a05cb0e17323cdb5096dfac8388e71531f313d630b294344e92e8 +size 59042 diff --git "a/ANote/image/PBR\347\254\224\350\256\260/2021-12-07-15-17-18.png" "b/ANote/image/PBR\347\254\224\350\256\260/2021-12-07-15-17-18.png" new file mode 100644 index 00000000..28280293 --- /dev/null +++ "b/ANote/image/PBR\347\254\224\350\256\260/2021-12-07-15-17-18.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7dd3093515eb37e815d324c6ac059e3799b6921c87fef6c5733c8af60ddf8ce6 +size 54689 diff --git "a/ANote/image/PBR\347\254\224\350\256\260/2021-12-07-15-31-14.png" "b/ANote/image/PBR\347\254\224\350\256\260/2021-12-07-15-31-14.png" new file mode 100644 index 00000000..4cca09da --- /dev/null +++ "b/ANote/image/PBR\347\254\224\350\256\260/2021-12-07-15-31-14.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:63b2837567b02de6a21f98f3d6e74c6d403bd36c03ffc6ede2e76449ba00981e +size 100437 diff --git "a/ANote/image/PBR\347\254\224\350\256\260/2021-12-07-15-31-37.png" "b/ANote/image/PBR\347\254\224\350\256\260/2021-12-07-15-31-37.png" new file mode 100644 index 00000000..be6e651f --- /dev/null +++ "b/ANote/image/PBR\347\254\224\350\256\260/2021-12-07-15-31-37.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:607c05496b9c4842b54db772d47a456d10b0eb123d0ac3977bfcf778a64b9dd9 +size 63271 diff --git "a/ANote/image/PBR\347\254\224\350\256\260/2021-12-07-15-32-26.png" "b/ANote/image/PBR\347\254\224\350\256\260/2021-12-07-15-32-26.png" new file mode 100644 index 00000000..3dcd5c90 --- /dev/null +++ "b/ANote/image/PBR\347\254\224\350\256\260/2021-12-07-15-32-26.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b1179fe68a100aac3d4c2a612b3d170a0d6e826c0aafc423decd3c32a4c142d6 +size 27791 diff --git "a/ANote/image/PBR\347\254\224\350\256\260/2021-12-07-15-33-40.png" "b/ANote/image/PBR\347\254\224\350\256\260/2021-12-07-15-33-40.png" new file mode 100644 index 00000000..748cc794 --- /dev/null +++ "b/ANote/image/PBR\347\254\224\350\256\260/2021-12-07-15-33-40.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b9987d730311dd02ba925c7f05dcb7fa24873291ec569bdb067dd93a5d8d7576 +size 52378 diff --git "a/ANote/image/PBR\347\254\224\350\256\260/2021-12-07-15-34-30.png" "b/ANote/image/PBR\347\254\224\350\256\260/2021-12-07-15-34-30.png" new file mode 100644 index 00000000..47639869 --- /dev/null +++ "b/ANote/image/PBR\347\254\224\350\256\260/2021-12-07-15-34-30.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ba68fbadae84334da827eb704600a5d3750122796b44812a269cc7d1902b04ec +size 32019 diff --git "a/ANote/image/PBR\347\254\224\350\256\260/2021-12-07-15-36-35.png" "b/ANote/image/PBR\347\254\224\350\256\260/2021-12-07-15-36-35.png" new file mode 100644 index 00000000..bfeb5c10 --- /dev/null +++ "b/ANote/image/PBR\347\254\224\350\256\260/2021-12-07-15-36-35.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:391ae3f14feaac768aa829fbc45adecf650f1dd1f6dfb3bd0bbcff869e4ec0db +size 54089 diff --git "a/ANote/image/PBR\347\254\224\350\256\260/2021-12-07-15-40-34.png" "b/ANote/image/PBR\347\254\224\350\256\260/2021-12-07-15-40-34.png" new file mode 100644 index 00000000..e08eb67c --- /dev/null +++ "b/ANote/image/PBR\347\254\224\350\256\260/2021-12-07-15-40-34.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:df48b61af7f0a71542553045471d13c116594c352b2c331ae0d41aabae7036e9 +size 388501 diff --git "a/ANote/image/PBR\347\254\224\350\256\260/2021-12-07-16-26-09.png" "b/ANote/image/PBR\347\254\224\350\256\260/2021-12-07-16-26-09.png" new file mode 100644 index 00000000..b1cfdc90 --- /dev/null +++ "b/ANote/image/PBR\347\254\224\350\256\260/2021-12-07-16-26-09.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8a150c8c017fe0ddb84acbf8e4f40d013cf359663f5f728f3053d2bde76f330e +size 58253 diff --git "a/ANote/image/PBR\347\254\224\350\256\260/2021-12-07-16-46-20.png" "b/ANote/image/PBR\347\254\224\350\256\260/2021-12-07-16-46-20.png" new file mode 100644 index 00000000..7ed4e5cc --- /dev/null +++ "b/ANote/image/PBR\347\254\224\350\256\260/2021-12-07-16-46-20.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e5e75f94de5ec84b5b91640c9d697ee350dc653cf16f10043a3a867f4f7b9735 +size 26527 diff --git "a/ANote/image/PBR\347\254\224\350\256\260/2021-12-07-16-50-08.png" "b/ANote/image/PBR\347\254\224\350\256\260/2021-12-07-16-50-08.png" new file mode 100644 index 00000000..e5b93f2a --- /dev/null +++ "b/ANote/image/PBR\347\254\224\350\256\260/2021-12-07-16-50-08.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:56d26688e1c535ebc6a8beea4577009267d5dce8b873ba3d2aa7c093d8059b71 +size 82098 diff --git "a/ANote/image/PBR\347\254\224\350\256\260/2021-12-07-16-50-29.png" "b/ANote/image/PBR\347\254\224\350\256\260/2021-12-07-16-50-29.png" new file mode 100644 index 00000000..47ecdfd2 --- /dev/null +++ "b/ANote/image/PBR\347\254\224\350\256\260/2021-12-07-16-50-29.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:32d4bce0c2ecb7bd9fbf3de2bdabc9a67d81bd92c61ab6f65cd22a22fa90b687 +size 830998 diff --git "a/ANote/image/PBR\347\254\224\350\256\260/2021-12-07-17-47-43.png" "b/ANote/image/PBR\347\254\224\350\256\260/2021-12-07-17-47-43.png" new file mode 100644 index 00000000..4a19e93b --- /dev/null +++ "b/ANote/image/PBR\347\254\224\350\256\260/2021-12-07-17-47-43.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8ac371f25cdf2790c7a42e05ccc3256d769a2daf4bc6cc12a7e14ea02413d104 +size 6162 diff --git "a/ANote/image/PBR\347\254\224\350\256\260/2021-12-07-17-48-28.png" "b/ANote/image/PBR\347\254\224\350\256\260/2021-12-07-17-48-28.png" new file mode 100644 index 00000000..a87fa67c --- /dev/null +++ "b/ANote/image/PBR\347\254\224\350\256\260/2021-12-07-17-48-28.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e52106f4a1c6444e7438e30090654de78376b6202b350f1a0f6bdd642c01edac +size 90421 diff --git "a/ANote/image/PBR\347\254\224\350\256\260/2021-12-07-17-57-45.png" "b/ANote/image/PBR\347\254\224\350\256\260/2021-12-07-17-57-45.png" new file mode 100644 index 00000000..7bf29df7 --- /dev/null +++ "b/ANote/image/PBR\347\254\224\350\256\260/2021-12-07-17-57-45.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:713d8fa259e99584c5682f9b22e3880ebbc0741a2cc2731dbfed77f7def30d00 +size 48575 diff --git "a/ANote/image/PBR\347\254\224\350\256\260/2021-12-07-18-04-14.png" "b/ANote/image/PBR\347\254\224\350\256\260/2021-12-07-18-04-14.png" new file mode 100644 index 00000000..ac8bbfae --- /dev/null +++ "b/ANote/image/PBR\347\254\224\350\256\260/2021-12-07-18-04-14.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:963c286f156bb2142ba65291d14d38ac86942eee82bd6239400533c42fb3581c +size 13823 diff --git "a/ANote/image/PBR\347\254\224\350\256\260/2021-12-07-18-21-07.png" "b/ANote/image/PBR\347\254\224\350\256\260/2021-12-07-18-21-07.png" new file mode 100644 index 00000000..89ab3016 --- /dev/null +++ "b/ANote/image/PBR\347\254\224\350\256\260/2021-12-07-18-21-07.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:42aa30b7861bad91163fdf7491dd6b21c37e5853f202b9dda645ecce5cbfb89a +size 4424 diff --git "a/ANote/image/PBR\347\254\224\350\256\260/2021-12-07-18-23-31.png" "b/ANote/image/PBR\347\254\224\350\256\260/2021-12-07-18-23-31.png" new file mode 100644 index 00000000..d84a8f45 --- /dev/null +++ "b/ANote/image/PBR\347\254\224\350\256\260/2021-12-07-18-23-31.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:04ea39762d2f4417f7add15ee035ad8f4637e00fdd5594ed5881ef660c94aeda +size 2763 diff --git "a/ANote/image/PBR\347\254\224\350\256\260/2021-12-07-18-25-21.png" "b/ANote/image/PBR\347\254\224\350\256\260/2021-12-07-18-25-21.png" new file mode 100644 index 00000000..b5a709fe --- /dev/null +++ "b/ANote/image/PBR\347\254\224\350\256\260/2021-12-07-18-25-21.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:24b75f68d22a5148ebbe18622b20d5bcf862bd2336269c965cdf15291a9910e1 +size 5326 diff --git "a/ANote/image/PBR\347\254\224\350\256\260/2021-12-07-18-30-45.png" "b/ANote/image/PBR\347\254\224\350\256\260/2021-12-07-18-30-45.png" new file mode 100644 index 00000000..aa59788d --- /dev/null +++ "b/ANote/image/PBR\347\254\224\350\256\260/2021-12-07-18-30-45.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e6e62421b537b6b5d62ee1ce296fd4d86ea77b7c5da6f912f4be39e7956158a8 +size 6628 diff --git "a/ANote/image/PBR\347\254\224\350\256\260/2021-12-07-18-31-03.png" "b/ANote/image/PBR\347\254\224\350\256\260/2021-12-07-18-31-03.png" new file mode 100644 index 00000000..13f0f9c3 --- /dev/null +++ "b/ANote/image/PBR\347\254\224\350\256\260/2021-12-07-18-31-03.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7beaba230f20d12af3b731c6f7e0bbb24d0f832a5a355203a1162f7dc1a0e87e +size 28808 diff --git "a/ANote/image/PBR\347\254\224\350\256\260/2021-12-07-18-32-18.png" "b/ANote/image/PBR\347\254\224\350\256\260/2021-12-07-18-32-18.png" new file mode 100644 index 00000000..1a56381b --- /dev/null +++ "b/ANote/image/PBR\347\254\224\350\256\260/2021-12-07-18-32-18.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ab91e27f054106d8cc0059b1057c28d454ad51e07c22eac9432b0df550593a5e +size 5590 diff --git "a/ANote/image/PBR\347\254\224\350\256\260/2021-12-07-18-33-11.png" "b/ANote/image/PBR\347\254\224\350\256\260/2021-12-07-18-33-11.png" new file mode 100644 index 00000000..e7f25879 --- /dev/null +++ "b/ANote/image/PBR\347\254\224\350\256\260/2021-12-07-18-33-11.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5eddd7e0a0054d62117b29c53aaa6235ba2421ac30a6aba5bc2204e618d16c0e +size 18201 diff --git "a/ANote/image/PBR\347\254\224\350\256\260/2021-12-07-18-33-41.png" "b/ANote/image/PBR\347\254\224\350\256\260/2021-12-07-18-33-41.png" new file mode 100644 index 00000000..1dfb60b9 --- /dev/null +++ "b/ANote/image/PBR\347\254\224\350\256\260/2021-12-07-18-33-41.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:56cfb070b240bd32136bc4e617f0af36f328ff8b1391e43e38495ea313028089 +size 107399 diff --git "a/ANote/image/PBR\347\254\224\350\256\260/2021-12-07-18-37-23.png" "b/ANote/image/PBR\347\254\224\350\256\260/2021-12-07-18-37-23.png" new file mode 100644 index 00000000..ce810c37 --- /dev/null +++ "b/ANote/image/PBR\347\254\224\350\256\260/2021-12-07-18-37-23.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3fcf30c147ddaa6e862ab7d3e93257aadb23d7fe632a991342137b92e6ee9fe0 +size 25456 diff --git "a/ANote/image/PBR\347\254\224\350\256\260/2021-12-07-18-37-52.png" "b/ANote/image/PBR\347\254\224\350\256\260/2021-12-07-18-37-52.png" new file mode 100644 index 00000000..eed7b4c9 --- /dev/null +++ "b/ANote/image/PBR\347\254\224\350\256\260/2021-12-07-18-37-52.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9e4553c567a4ce0d643eb3df442e723ee87f17cfdc1804e5085fef6b4e656f73 +size 5785 diff --git "a/ANote/image/PBR\347\254\224\350\256\260/2021-12-07-18-38-23.png" "b/ANote/image/PBR\347\254\224\350\256\260/2021-12-07-18-38-23.png" new file mode 100644 index 00000000..a039d97c --- /dev/null +++ "b/ANote/image/PBR\347\254\224\350\256\260/2021-12-07-18-38-23.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:92f9ce8339f6c131a7b1fb22345c8a23be623dbd38375d7f05ba8c0debe5cfc8 +size 4751 diff --git "a/ANote/image/PBR\347\254\224\350\256\260/2021-12-07-18-38-50.png" "b/ANote/image/PBR\347\254\224\350\256\260/2021-12-07-18-38-50.png" new file mode 100644 index 00000000..994b6844 --- /dev/null +++ "b/ANote/image/PBR\347\254\224\350\256\260/2021-12-07-18-38-50.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1da5d62ff3e21e3d138ddf755cd45d3c8641c0925291bf6b92f8e99b2ae20c00 +size 5010 diff --git "a/ANote/image/PBR\347\254\224\350\256\260/2021-12-07-18-39-27.png" "b/ANote/image/PBR\347\254\224\350\256\260/2021-12-07-18-39-27.png" new file mode 100644 index 00000000..2f65fc15 --- /dev/null +++ "b/ANote/image/PBR\347\254\224\350\256\260/2021-12-07-18-39-27.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2e82172567def33f0749da48203572b51b4674dbf74c2971016ee0287dd8eabb +size 40438 diff --git "a/ANote/image/PBR\347\254\224\350\256\260/2021-12-07-18-41-31.png" "b/ANote/image/PBR\347\254\224\350\256\260/2021-12-07-18-41-31.png" new file mode 100644 index 00000000..656d6815 --- /dev/null +++ "b/ANote/image/PBR\347\254\224\350\256\260/2021-12-07-18-41-31.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:abfc8b305ddf509c85b9973c9f41e7f5fad16a36f1fbf88bb315440c77f68c32 +size 8928 diff --git "a/ANote/image/PBR\347\254\224\350\256\260/2021-12-07-18-42-16.png" "b/ANote/image/PBR\347\254\224\350\256\260/2021-12-07-18-42-16.png" new file mode 100644 index 00000000..467b4516 --- /dev/null +++ "b/ANote/image/PBR\347\254\224\350\256\260/2021-12-07-18-42-16.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:dcfb4fa1683b80147147d58e6b11407b0cbe92089da2be0c76be0c4ec0ddc767 +size 8882 diff --git "a/ANote/image/PBR\347\254\224\350\256\260/2021-12-07-18-46-42.png" "b/ANote/image/PBR\347\254\224\350\256\260/2021-12-07-18-46-42.png" new file mode 100644 index 00000000..bce5d405 --- /dev/null +++ "b/ANote/image/PBR\347\254\224\350\256\260/2021-12-07-18-46-42.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2071e95517746520bef4151b2dd4e2217616cfb985a284d93375614b40f407f0 +size 36616 diff --git "a/ANote/image/PBR\347\254\224\350\256\260/2021-12-09-14-51-16.png" "b/ANote/image/PBR\347\254\224\350\256\260/2021-12-09-14-51-16.png" new file mode 100644 index 00000000..b33728e5 --- /dev/null +++ "b/ANote/image/PBR\347\254\224\350\256\260/2021-12-09-14-51-16.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c98b689036ef74ba8ad152326d279ad557d2470b05f3e120dfa9070de50d2384 +size 10129 diff --git "a/ANote/image/PBR\347\254\224\350\256\260/2021-12-09-14-53-27.png" "b/ANote/image/PBR\347\254\224\350\256\260/2021-12-09-14-53-27.png" new file mode 100644 index 00000000..d51884d3 --- /dev/null +++ "b/ANote/image/PBR\347\254\224\350\256\260/2021-12-09-14-53-27.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f41ea064c99688fb7a0640b8cb390bd4883d5c6f9bf844ef6834a873d79037bd +size 6004 diff --git "a/ANote/image/PBR\347\254\224\350\256\260/2021-12-09-14-55-30.png" "b/ANote/image/PBR\347\254\224\350\256\260/2021-12-09-14-55-30.png" new file mode 100644 index 00000000..32feb51b --- /dev/null +++ "b/ANote/image/PBR\347\254\224\350\256\260/2021-12-09-14-55-30.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8b905382f9d371604a3539a971c6ab15c685a24914cb0e1d1b62661354346e7f +size 34499 diff --git "a/ANote/image/PBR\347\254\224\350\256\260/2021-12-09-14-56-35.png" "b/ANote/image/PBR\347\254\224\350\256\260/2021-12-09-14-56-35.png" new file mode 100644 index 00000000..2a988608 --- /dev/null +++ "b/ANote/image/PBR\347\254\224\350\256\260/2021-12-09-14-56-35.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4da19a02fa155072a871ccfcba29e53dad090efd154f770587ede101cc695bf3 +size 218221 diff --git "a/ANote/image/PBR\347\254\224\350\256\260/2021-12-09-15-09-10.png" "b/ANote/image/PBR\347\254\224\350\256\260/2021-12-09-15-09-10.png" new file mode 100644 index 00000000..62e82cba --- /dev/null +++ "b/ANote/image/PBR\347\254\224\350\256\260/2021-12-09-15-09-10.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ceb542f99837b41c275ea0596b89d49f254cf1baa87c02af710a71992e41f6b2 +size 61794 diff --git "a/ANote/image/PBR\347\254\224\350\256\260/2021-12-09-15-11-57.png" "b/ANote/image/PBR\347\254\224\350\256\260/2021-12-09-15-11-57.png" new file mode 100644 index 00000000..0a0dec40 --- /dev/null +++ "b/ANote/image/PBR\347\254\224\350\256\260/2021-12-09-15-11-57.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fdea87f7bf0e510f7f25da5999fb7e9ee7e58629d9c01b33a1a51ca57595f0c3 +size 399284 diff --git "a/ANote/image/PBR\347\254\224\350\256\260/2021-12-09-15-15-13.png" "b/ANote/image/PBR\347\254\224\350\256\260/2021-12-09-15-15-13.png" new file mode 100644 index 00000000..ae4baedd --- /dev/null +++ "b/ANote/image/PBR\347\254\224\350\256\260/2021-12-09-15-15-13.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:36d354891cfb917614accb8dc62825b8a8089aff59bc85ed0af9407a11e12585 +size 12200 diff --git "a/ANote/image/PBR\347\254\224\350\256\260/2021-12-09-15-15-43.png" "b/ANote/image/PBR\347\254\224\350\256\260/2021-12-09-15-15-43.png" new file mode 100644 index 00000000..bfc3c3d0 --- /dev/null +++ "b/ANote/image/PBR\347\254\224\350\256\260/2021-12-09-15-15-43.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8a34f21bd0988e90cbef818815acd4f7f60fc985f7709f28e5176bf661073d7f +size 5555 diff --git "a/ANote/image/PBR\347\254\224\350\256\260/2021-12-09-15-16-11.png" "b/ANote/image/PBR\347\254\224\350\256\260/2021-12-09-15-16-11.png" new file mode 100644 index 00000000..e70be74a --- /dev/null +++ "b/ANote/image/PBR\347\254\224\350\256\260/2021-12-09-15-16-11.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:38ae2759b4d7289150ff9d6ea924ead50f4c2758afdb0bce9464d6e431ac08ba +size 17976 diff --git "a/ANote/image/PBR\347\254\224\350\256\260/2021-12-09-15-18-33.png" "b/ANote/image/PBR\347\254\224\350\256\260/2021-12-09-15-18-33.png" new file mode 100644 index 00000000..0d2a3670 --- /dev/null +++ "b/ANote/image/PBR\347\254\224\350\256\260/2021-12-09-15-18-33.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:147dab91226462d4a6f783e791d136ed272295b6cb9bcb7d98ed9aa352bbb737 +size 4553 diff --git "a/ANote/image/PBR\347\254\224\350\256\260/2021-12-09-15-19-09.png" "b/ANote/image/PBR\347\254\224\350\256\260/2021-12-09-15-19-09.png" new file mode 100644 index 00000000..21db4a13 --- /dev/null +++ "b/ANote/image/PBR\347\254\224\350\256\260/2021-12-09-15-19-09.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d51d5d4469f9f79393b83cfbe1462ff27b119db8e94793d6e9c72d9812480787 +size 338462 diff --git "a/ANote/image/PBR\347\254\224\350\256\260/2021-12-09-15-20-38.png" "b/ANote/image/PBR\347\254\224\350\256\260/2021-12-09-15-20-38.png" new file mode 100644 index 00000000..b2822883 --- /dev/null +++ "b/ANote/image/PBR\347\254\224\350\256\260/2021-12-09-15-20-38.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:948bc102014a3e7d6581b1389721200b7c63973ade738b1c37b2e2fc4a08b568 +size 138237 diff --git "a/ANote/image/PBR\347\254\224\350\256\260/2021-12-09-15-21-14.png" "b/ANote/image/PBR\347\254\224\350\256\260/2021-12-09-15-21-14.png" new file mode 100644 index 00000000..a29c2570 --- /dev/null +++ "b/ANote/image/PBR\347\254\224\350\256\260/2021-12-09-15-21-14.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c89d03edcbf5e807b01f8434a46276069b8155a1d6f481ee3790bc02793a7068 +size 3853 diff --git "a/ANote/image/PBR\347\254\224\350\256\260/2021-12-09-15-21-23.png" "b/ANote/image/PBR\347\254\224\350\256\260/2021-12-09-15-21-23.png" new file mode 100644 index 00000000..a808e8d3 --- /dev/null +++ "b/ANote/image/PBR\347\254\224\350\256\260/2021-12-09-15-21-23.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4bc7480a63998286f6d8965e0bdca4b4baf4defb515db635a2812db1a013c764 +size 81727 diff --git "a/ANote/image/PBR\347\254\224\350\256\260/2021-12-20-00-49-49.png" "b/ANote/image/PBR\347\254\224\350\256\260/2021-12-20-00-49-49.png" new file mode 100644 index 00000000..6ea7fc79 --- /dev/null +++ "b/ANote/image/PBR\347\254\224\350\256\260/2021-12-20-00-49-49.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:16ab8c9c1ef4031ed3090e609f8dffe30438dabdbd77ab4ce80bb3069fd31b63 +size 25610 diff --git "a/ANote/image/PBR\347\254\224\350\256\260/9m462PagJLhINVT.png" "b/ANote/image/PBR\347\254\224\350\256\260/9m462PagJLhINVT.png" new file mode 100644 index 00000000..b0920e9a --- /dev/null +++ "b/ANote/image/PBR\347\254\224\350\256\260/9m462PagJLhINVT.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:51dba8de672514ef32cc830317db0dc1c24f06360b565096c0f84718114b247f +size 24084 diff --git "a/ANote/image/PBR\347\254\224\350\256\260/Opwz13retCNbZWF.png" "b/ANote/image/PBR\347\254\224\350\256\260/Opwz13retCNbZWF.png" new file mode 100644 index 00000000..637d99e6 --- /dev/null +++ "b/ANote/image/PBR\347\254\224\350\256\260/Opwz13retCNbZWF.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:26cf325191923480bd29c5d44a40597be0e52c375cad9464de0d61b6a4f05416 +size 1217 diff --git "a/ANote/image/PBR\347\254\224\350\256\260/UZhIjWi5yXzdLno.png" "b/ANote/image/PBR\347\254\224\350\256\260/UZhIjWi5yXzdLno.png" new file mode 100644 index 00000000..126c1e20 --- /dev/null +++ "b/ANote/image/PBR\347\254\224\350\256\260/UZhIjWi5yXzdLno.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:352aa48557535e78872c7b0ed4b2ca3e6579a516b431414715211a14803387a5 +size 16052 diff --git "a/ANote/image/PBR\347\254\224\350\256\260/pbr.jpg" "b/ANote/image/PBR\347\254\224\350\256\260/pbr.jpg" new file mode 100644 index 00000000..ecdabfd8 --- /dev/null +++ "b/ANote/image/PBR\347\254\224\350\256\260/pbr.jpg" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:15cf4c97bd9e2391bfe976239128260e7a39463d2349f0cfb85c7a5c990e4c80 +size 480797 diff --git "a/ANote/image/PBR\347\254\224\350\256\260/vPOtJjhr21gLTfD.png" "b/ANote/image/PBR\347\254\224\350\256\260/vPOtJjhr21gLTfD.png" new file mode 100644 index 00000000..82f12360 --- /dev/null +++ "b/ANote/image/PBR\347\254\224\350\256\260/vPOtJjhr21gLTfD.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fd9af385103e2ed9ecd1b525b54aa0664102e4703e65faf6d7f9cdd618372870 +size 5741 diff --git "a/ANote/image/ParticleEffectForUGUI\344\275\277\347\224\250\346\225\231\347\250\213/2022-02-17-17-23-49.png" "b/ANote/image/ParticleEffectForUGUI\344\275\277\347\224\250\346\225\231\347\250\213/2022-02-17-17-23-49.png" new file mode 100644 index 00000000..70722059 --- /dev/null +++ "b/ANote/image/ParticleEffectForUGUI\344\275\277\347\224\250\346\225\231\347\250\213/2022-02-17-17-23-49.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:167436978bcafae5c4d0ce1f4969f30824f199fd325bc4cfad41c5310b7b906d +size 92768 diff --git "a/ANote/image/ParticleEffectForUGUI\344\275\277\347\224\250\346\225\231\347\250\213/2022-02-17-17-29-55.png" "b/ANote/image/ParticleEffectForUGUI\344\275\277\347\224\250\346\225\231\347\250\213/2022-02-17-17-29-55.png" new file mode 100644 index 00000000..d18d772e --- /dev/null +++ "b/ANote/image/ParticleEffectForUGUI\344\275\277\347\224\250\346\225\231\347\250\213/2022-02-17-17-29-55.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b2612bfddc2acab6b610bb9f1aa7753882307081eb30aece3dcaec1197f6534c +size 107247 diff --git "a/ANote/image/SSH\347\254\224\350\256\260/2021-12-21-17-31-54.png" "b/ANote/image/SSH\347\254\224\350\256\260/2021-12-21-17-31-54.png" new file mode 100644 index 00000000..07359fe0 --- /dev/null +++ "b/ANote/image/SSH\347\254\224\350\256\260/2021-12-21-17-31-54.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:94e91a3584683fea72d06f7c0fd7bd843f1d5860276e7e9023097582bbb406e8 +size 96734 diff --git "a/ANote/image/SSH\347\254\224\350\256\260/2024-03-12-20-52-37.png" "b/ANote/image/SSH\347\254\224\350\256\260/2024-03-12-20-52-37.png" new file mode 100644 index 00000000..b6026e21 --- /dev/null +++ "b/ANote/image/SSH\347\254\224\350\256\260/2024-03-12-20-52-37.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7570bd3e71d97689618e00975dd1b966ec7bacaa52ffa903dc0d0f52ed6a4db7 +size 53207 diff --git "a/ANote/image/Shader\346\241\210\344\276\213/2021-12-21-20-18-01.png" "b/ANote/image/Shader\346\241\210\344\276\213/2021-12-21-20-18-01.png" new file mode 100644 index 00000000..5d77164a --- /dev/null +++ "b/ANote/image/Shader\346\241\210\344\276\213/2021-12-21-20-18-01.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d9459bcd08d61a44a65bbdd4472f0d3599c172a6df18a71643b20f2cb462ac97 +size 3034214 diff --git "a/ANote/image/Shader\346\241\210\344\276\213/CleanShot2022-03-17.gif" "b/ANote/image/Shader\346\241\210\344\276\213/CleanShot2022-03-17.gif" new file mode 100644 index 00000000..936b1189 --- /dev/null +++ "b/ANote/image/Shader\346\241\210\344\276\213/CleanShot2022-03-17.gif" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:18c0a56c4f79f2f0446ec4634c681c4bcd1c7485987a6dc3a547f0e187005987 +size 4560072 diff --git "a/ANote/image/Shader\346\241\210\344\276\213/avatorshow.gif" "b/ANote/image/Shader\346\241\210\344\276\213/avatorshow.gif" new file mode 100644 index 00000000..4339cbb7 --- /dev/null +++ "b/ANote/image/Shader\346\241\210\344\276\213/avatorshow.gif" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:421c9bc15bccf44d90aeddaab45f0c8ab7d084a5bdbcfbba40073eb4e7cd1dcf +size 37434330 diff --git "a/ANote/image/Shader\347\254\224\350\256\260/2021-11-25-20-06-28.png" "b/ANote/image/Shader\347\254\224\350\256\260/2021-11-25-20-06-28.png" new file mode 100644 index 00000000..2e631646 --- /dev/null +++ "b/ANote/image/Shader\347\254\224\350\256\260/2021-11-25-20-06-28.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:257bf5f52da36b174aa072a807b7ddb212221c558e1444128c3292b3be361f10 +size 149971 diff --git "a/ANote/image/Shader\347\254\224\350\256\260/2021-11-25-20-08-03.png" "b/ANote/image/Shader\347\254\224\350\256\260/2021-11-25-20-08-03.png" new file mode 100644 index 00000000..fdf35276 --- /dev/null +++ "b/ANote/image/Shader\347\254\224\350\256\260/2021-11-25-20-08-03.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:afd7d0c4218756180dc7db176e7d946b59344d99c0a7a5b377c10edf41f64260 +size 8001 diff --git "a/ANote/image/Shader\347\254\224\350\256\260/2021-11-25-20-09-52.png" "b/ANote/image/Shader\347\254\224\350\256\260/2021-11-25-20-09-52.png" new file mode 100644 index 00000000..558c9440 --- /dev/null +++ "b/ANote/image/Shader\347\254\224\350\256\260/2021-11-25-20-09-52.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0c7f5deec3a12a5dd641074213faed873fc959de0a27d91b0e41aa0a2b70e05f +size 80664 diff --git "a/ANote/image/Shader\347\254\224\350\256\260/CsUA4dOkTS6XHa3.png" "b/ANote/image/Shader\347\254\224\350\256\260/CsUA4dOkTS6XHa3.png" new file mode 100644 index 00000000..ff2ea152 --- /dev/null +++ "b/ANote/image/Shader\347\254\224\350\256\260/CsUA4dOkTS6XHa3.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cf6d52806193ccadd02e48c0d87d39d8ae777c579a136a98578e4107a9162520 +size 47952 diff --git "a/ANote/image/Shader\347\254\224\350\256\260/EwVRcDFQgfeu9qL.png" "b/ANote/image/Shader\347\254\224\350\256\260/EwVRcDFQgfeu9qL.png" new file mode 100644 index 00000000..39ca7577 --- /dev/null +++ "b/ANote/image/Shader\347\254\224\350\256\260/EwVRcDFQgfeu9qL.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3986182d23450b10c93091ebabaa6a36876ca8dca18120e0fbde1ec91458e4d1 +size 47350 diff --git "a/ANote/image/Shader\347\254\224\350\256\260/gD4rOtGw7CJzkUe.png" "b/ANote/image/Shader\347\254\224\350\256\260/gD4rOtGw7CJzkUe.png" new file mode 100644 index 00000000..785299a3 --- /dev/null +++ "b/ANote/image/Shader\347\254\224\350\256\260/gD4rOtGw7CJzkUe.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7d9b32f027084fcd77ea13b0cb9750b45a2b6292434b4e4185926f62a86893c7 +size 47364 diff --git "a/ANote/image/Shader\347\254\224\350\256\260/niCG5StoMDuUzlR.png" "b/ANote/image/Shader\347\254\224\350\256\260/niCG5StoMDuUzlR.png" new file mode 100644 index 00000000..4cf6234f --- /dev/null +++ "b/ANote/image/Shader\347\254\224\350\256\260/niCG5StoMDuUzlR.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fa4615f8733a68909745660d44318577405d43a469fdcbb6f0074bdc48f8668a +size 379317 diff --git "a/ANote/image/Shell\347\254\224\350\256\260/2024-04-11-17-56-44.png" "b/ANote/image/Shell\347\254\224\350\256\260/2024-04-11-17-56-44.png" new file mode 100644 index 00000000..e6aa4f3d --- /dev/null +++ "b/ANote/image/Shell\347\254\224\350\256\260/2024-04-11-17-56-44.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:56d4a9a2bfb422bd6967c6e8367c7037346c32f12fd41f2355e124fdf1f48152 +size 14217 diff --git "a/ANote/image/Shell\347\254\224\350\256\260/2024-04-11-17-56-50.png" "b/ANote/image/Shell\347\254\224\350\256\260/2024-04-11-17-56-50.png" new file mode 100644 index 00000000..edbaac1c --- /dev/null +++ "b/ANote/image/Shell\347\254\224\350\256\260/2024-04-11-17-56-50.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4f836b20dcbdd33f38ee087b0ecf321cabfe05309836adb3b6d6dbe8e1e33406 +size 16629 diff --git "a/ANote/image/Shell\347\254\224\350\256\260/2024-04-11-17-56-54.png" "b/ANote/image/Shell\347\254\224\350\256\260/2024-04-11-17-56-54.png" new file mode 100644 index 00000000..2af372ca --- /dev/null +++ "b/ANote/image/Shell\347\254\224\350\256\260/2024-04-11-17-56-54.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6035b0c4fa9c044fcac09e6ebbb51c654f0a8f7b40853bba9877a9a70ed4be79 +size 40752 diff --git "a/ANote/image/Shell\347\254\224\350\256\260/2024-04-11-17-57-05.png" "b/ANote/image/Shell\347\254\224\350\256\260/2024-04-11-17-57-05.png" new file mode 100644 index 00000000..c90f5211 --- /dev/null +++ "b/ANote/image/Shell\347\254\224\350\256\260/2024-04-11-17-57-05.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d2eeaa39853d5d20ebfaa909cd800049102e0ee2069bbf438458e945878c3154 +size 28160 diff --git "a/ANote/image/UI\347\254\224\350\256\260/2022-10-24-11-51-22.png" "b/ANote/image/UI\347\254\224\350\256\260/2022-10-24-11-51-22.png" new file mode 100644 index 00000000..c4682758 --- /dev/null +++ "b/ANote/image/UI\347\254\224\350\256\260/2022-10-24-11-51-22.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cc2bd5547e9de0f41a54b4293d01c4d966026b9976196c8cb68644f64b42d1b9 +size 25370 diff --git "a/ANote/image/UI\347\254\224\350\256\260/2022-10-25-11-21-43.png" "b/ANote/image/UI\347\254\224\350\256\260/2022-10-25-11-21-43.png" new file mode 100644 index 00000000..5d7c83a0 --- /dev/null +++ "b/ANote/image/UI\347\254\224\350\256\260/2022-10-25-11-21-43.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:be0318da6c5102b99b20d18dcce47bb34de2f50bb18c061eb1e276e676e3ad87 +size 272408 diff --git "a/ANote/image/UI\347\254\224\350\256\260/2022-10-25-11-22-47.png" "b/ANote/image/UI\347\254\224\350\256\260/2022-10-25-11-22-47.png" new file mode 100644 index 00000000..19a9e02d --- /dev/null +++ "b/ANote/image/UI\347\254\224\350\256\260/2022-10-25-11-22-47.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e8599f1b44dfeb78606e4f46cec9be17f07969f5b192af8b8a3e3dbc0c654275 +size 8994 diff --git "a/ANote/image/UI\347\254\224\350\256\260/2022-10-25-11-25-46.png" "b/ANote/image/UI\347\254\224\350\256\260/2022-10-25-11-25-46.png" new file mode 100644 index 00000000..d0bd1055 --- /dev/null +++ "b/ANote/image/UI\347\254\224\350\256\260/2022-10-25-11-25-46.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c88868a32864d0ddbc419d85f9cb5e24efaa1e33f34ce8722d4a79a695749e82 +size 306222 diff --git "a/ANote/image/UI\347\254\224\350\256\260/2022-10-25-11-26-26.png" "b/ANote/image/UI\347\254\224\350\256\260/2022-10-25-11-26-26.png" new file mode 100644 index 00000000..25b75e62 --- /dev/null +++ "b/ANote/image/UI\347\254\224\350\256\260/2022-10-25-11-26-26.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4f69c3aa0133d251ac71abc19cb7508dc7a74111fbb63ffcff1c2dd1ce2df4f4 +size 257569 diff --git "a/ANote/image/UI\347\254\224\350\256\260/2022-10-25-11-26-57.png" "b/ANote/image/UI\347\254\224\350\256\260/2022-10-25-11-26-57.png" new file mode 100644 index 00000000..6dcb76c7 --- /dev/null +++ "b/ANote/image/UI\347\254\224\350\256\260/2022-10-25-11-26-57.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bc78bac87549ee7a90b5a9674873af4e819596f4553409ad0267119170efe95c +size 249225 diff --git "a/ANote/image/UI\347\254\224\350\256\260/2022-10-25-11-27-42.png" "b/ANote/image/UI\347\254\224\350\256\260/2022-10-25-11-27-42.png" new file mode 100644 index 00000000..8f7f20a0 --- /dev/null +++ "b/ANote/image/UI\347\254\224\350\256\260/2022-10-25-11-27-42.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0f9436acf82483107f49e1ea20c4adf23d23148791205c658fb490f856b30af6 +size 21062 diff --git "a/ANote/image/UI\347\254\224\350\256\260/2022-10-25-11-46-37.png" "b/ANote/image/UI\347\254\224\350\256\260/2022-10-25-11-46-37.png" new file mode 100644 index 00000000..f57c4d1d --- /dev/null +++ "b/ANote/image/UI\347\254\224\350\256\260/2022-10-25-11-46-37.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3805cfa2c2a43b637c63df62c57ecc9916aaae70361000af63668654fe019a4a +size 266923 diff --git "a/ANote/image/UI\347\254\224\350\256\260/2022-10-25-11-53-38.png" "b/ANote/image/UI\347\254\224\350\256\260/2022-10-25-11-53-38.png" new file mode 100644 index 00000000..529ebc1a --- /dev/null +++ "b/ANote/image/UI\347\254\224\350\256\260/2022-10-25-11-53-38.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:64d535224032bb3cfa7d46cfa3594454f8599db51bbe8cb67f546ac2f831712f +size 20887 diff --git "a/ANote/image/UI\347\254\224\350\256\260/2022-10-25-14-58-37.png" "b/ANote/image/UI\347\254\224\350\256\260/2022-10-25-14-58-37.png" new file mode 100644 index 00000000..0f07d932 --- /dev/null +++ "b/ANote/image/UI\347\254\224\350\256\260/2022-10-25-14-58-37.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:58ef2b2a342c145d4aa641cb896638db5c2677d9468c729ac2206614d902aee1 +size 73098 diff --git "a/ANote/image/UI\347\254\224\350\256\260/2022-10-25-15-37-17.png" "b/ANote/image/UI\347\254\224\350\256\260/2022-10-25-15-37-17.png" new file mode 100644 index 00000000..6460c3d3 --- /dev/null +++ "b/ANote/image/UI\347\254\224\350\256\260/2022-10-25-15-37-17.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:424f99d7e5a3a2dcf116b8d096f80edc32a3b9bbbad2ef7560be07dc0fa218e8 +size 22654 diff --git "a/ANote/image/UI\347\254\224\350\256\260/2022-10-25-15-40-49.png" "b/ANote/image/UI\347\254\224\350\256\260/2022-10-25-15-40-49.png" new file mode 100644 index 00000000..3b035b9d --- /dev/null +++ "b/ANote/image/UI\347\254\224\350\256\260/2022-10-25-15-40-49.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1dbea1921251001f915cce8cf20d0ae2257914ffa77d503d827c2a27e04ea3e2 +size 151431 diff --git "a/ANote/image/UI\347\254\224\350\256\260/2022-10-25-16-32-29.png" "b/ANote/image/UI\347\254\224\350\256\260/2022-10-25-16-32-29.png" new file mode 100644 index 00000000..2de0a92d --- /dev/null +++ "b/ANote/image/UI\347\254\224\350\256\260/2022-10-25-16-32-29.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8d78634d1d49b126a25b921cb9c24f093b438de803cec458d176a1372ef0574a +size 640634 diff --git "a/ANote/image/UI\347\254\224\350\256\260/2022-11-01-15-26-11.png" "b/ANote/image/UI\347\254\224\350\256\260/2022-11-01-15-26-11.png" new file mode 100644 index 00000000..89d4bf6b --- /dev/null +++ "b/ANote/image/UI\347\254\224\350\256\260/2022-11-01-15-26-11.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e548fbd837765b1af6a970c3bda68a9e56ca7b297f42614a3838195109b2f6a8 +size 194074 diff --git "a/ANote/image/UI\347\254\224\350\256\260/2023-01-05-16-19-12.png" "b/ANote/image/UI\347\254\224\350\256\260/2023-01-05-16-19-12.png" new file mode 100644 index 00000000..ea27b943 --- /dev/null +++ "b/ANote/image/UI\347\254\224\350\256\260/2023-01-05-16-19-12.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:133a11bddc3a2cd72a0c37030d44ede255b9799c265389bef63ceec7387824b0 +size 146801 diff --git "a/ANote/image/UI\347\254\224\350\256\260/2023-01-05-16-20-31.png" "b/ANote/image/UI\347\254\224\350\256\260/2023-01-05-16-20-31.png" new file mode 100644 index 00000000..fe98ec13 --- /dev/null +++ "b/ANote/image/UI\347\254\224\350\256\260/2023-01-05-16-20-31.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6571c24aafc1426f862c6e5c9af3c83eca8bc6a4ae749ef67a4d1a05e0823b58 +size 141515 diff --git "a/ANote/image/UI\347\254\224\350\256\260/2023-03-20-11-53-13.png" "b/ANote/image/UI\347\254\224\350\256\260/2023-03-20-11-53-13.png" new file mode 100644 index 00000000..1f3716d2 --- /dev/null +++ "b/ANote/image/UI\347\254\224\350\256\260/2023-03-20-11-53-13.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:583709dd4f086600c92984e107e5336441a481a223b53e4dec790ab49421872a +size 12376 diff --git "a/ANote/image/UI\347\254\224\350\256\260/2023-03-20-11-55-36.png" "b/ANote/image/UI\347\254\224\350\256\260/2023-03-20-11-55-36.png" new file mode 100644 index 00000000..362d6153 --- /dev/null +++ "b/ANote/image/UI\347\254\224\350\256\260/2023-03-20-11-55-36.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a011bc8c44c9ca4605e902bf2b9c873183c2941f068e2c65b6f074571e69ecc3 +size 14637 diff --git "a/ANote/image/UI\347\254\224\350\256\260/2023-03-20-14-27-51.png" "b/ANote/image/UI\347\254\224\350\256\260/2023-03-20-14-27-51.png" new file mode 100644 index 00000000..1f3e315f --- /dev/null +++ "b/ANote/image/UI\347\254\224\350\256\260/2023-03-20-14-27-51.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9ccd79daff37f09362a700821cd0b97e95b79d236a4fb384fce712b16a2c466f +size 14629 diff --git "a/ANote/image/UI\347\254\224\350\256\260/2023-03-20-14-30-38.png" "b/ANote/image/UI\347\254\224\350\256\260/2023-03-20-14-30-38.png" new file mode 100644 index 00000000..0e6bd0b7 --- /dev/null +++ "b/ANote/image/UI\347\254\224\350\256\260/2023-03-20-14-30-38.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0adf2d643b9cbe34a27dbeacabc411bfd2d22991771b39cf91bb2dfc2775b978 +size 14520 diff --git "a/ANote/image/UI\347\254\224\350\256\260/2023-03-20-14-31-49.png" "b/ANote/image/UI\347\254\224\350\256\260/2023-03-20-14-31-49.png" new file mode 100644 index 00000000..ee0cdbad --- /dev/null +++ "b/ANote/image/UI\347\254\224\350\256\260/2023-03-20-14-31-49.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f3ddd73d9752f47ce57db3732f77ea2633fada0cd1ab3f11c52843cd5daf1e39 +size 11946 diff --git "a/ANote/image/UI\347\254\224\350\256\260/2023-03-20-14-46-30.png" "b/ANote/image/UI\347\254\224\350\256\260/2023-03-20-14-46-30.png" new file mode 100644 index 00000000..60aa3f5b --- /dev/null +++ "b/ANote/image/UI\347\254\224\350\256\260/2023-03-20-14-46-30.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:53235c1c5079fbe3c7ed0729bc2bf102573bc62783009877c3fb91953859e2e0 +size 32730 diff --git "a/ANote/image/UI\347\254\224\350\256\260/2023-03-20-15-19-36.png" "b/ANote/image/UI\347\254\224\350\256\260/2023-03-20-15-19-36.png" new file mode 100644 index 00000000..658aafc9 --- /dev/null +++ "b/ANote/image/UI\347\254\224\350\256\260/2023-03-20-15-19-36.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2bbdbeb30ab65d598a27086f50ce32927917e0e211857dd81c0f140182be3cd7 +size 102141 diff --git "a/ANote/image/UI\347\254\224\350\256\260/2023-03-20-15-47-03.png" "b/ANote/image/UI\347\254\224\350\256\260/2023-03-20-15-47-03.png" new file mode 100644 index 00000000..674a8ab6 --- /dev/null +++ "b/ANote/image/UI\347\254\224\350\256\260/2023-03-20-15-47-03.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8c1277d9d550050992fb0dc9f05755dc2a66272e92c0196fbb0009debc0684d8 +size 150841 diff --git "a/ANote/image/UI\347\254\224\350\256\260/2023-03-20-20-57-53.png" "b/ANote/image/UI\347\254\224\350\256\260/2023-03-20-20-57-53.png" new file mode 100644 index 00000000..892c0d5c --- /dev/null +++ "b/ANote/image/UI\347\254\224\350\256\260/2023-03-20-20-57-53.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4d2cc733e6566252ed47f77640c4d164e36b399391c58c4ce8f7cc7848ffb0a5 +size 141214 diff --git "a/ANote/image/UI\347\254\224\350\256\260/2023-12-06-20-00-09.png" "b/ANote/image/UI\347\254\224\350\256\260/2023-12-06-20-00-09.png" new file mode 100644 index 00000000..c4a9b62a --- /dev/null +++ "b/ANote/image/UI\347\254\224\350\256\260/2023-12-06-20-00-09.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:deaa49e0929bfd70e850c5afecf1e6f66514b5d97cfc3a22c99dccf31aaf499f +size 9429 diff --git "a/ANote/image/UI\347\254\224\350\256\260/2024-03-13-14-40-51.png" "b/ANote/image/UI\347\254\224\350\256\260/2024-03-13-14-40-51.png" new file mode 100644 index 00000000..81cea342 --- /dev/null +++ "b/ANote/image/UI\347\254\224\350\256\260/2024-03-13-14-40-51.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:75c0f01ff38a76ce37bdf7068c6a1df655f20303fd744aa2f3f0a5bd14bc639d +size 400658 diff --git "a/ANote/image/UI\347\254\224\350\256\260/2024-03-13-14-41-58.png" "b/ANote/image/UI\347\254\224\350\256\260/2024-03-13-14-41-58.png" new file mode 100644 index 00000000..9fa42d7e --- /dev/null +++ "b/ANote/image/UI\347\254\224\350\256\260/2024-03-13-14-41-58.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b7d27e5a028ce96e5039db0264ccda3e9592fbecfead41c1569d8c0338bbd006 +size 563698 diff --git "a/ANote/image/UI\347\254\224\350\256\260/2024-03-13-14-49-07.png" "b/ANote/image/UI\347\254\224\350\256\260/2024-03-13-14-49-07.png" new file mode 100644 index 00000000..74417aeb --- /dev/null +++ "b/ANote/image/UI\347\254\224\350\256\260/2024-03-13-14-49-07.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:92ada2a8a463b79c0fa2c6930ce23df5c046f6c3ab596e48a2ea05747bcbc91f +size 556120 diff --git "a/ANote/image/UI\347\254\224\350\256\260/2024-03-13-15-02-46.png" "b/ANote/image/UI\347\254\224\350\256\260/2024-03-13-15-02-46.png" new file mode 100644 index 00000000..3934c8c6 --- /dev/null +++ "b/ANote/image/UI\347\254\224\350\256\260/2024-03-13-15-02-46.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bf3020ae40677b8689de4867c2808edd319935ddac0a566f62ca22255731e499 +size 812343 diff --git "a/ANote/image/UI\347\254\224\350\256\260/2024-03-13-15-04-54.png" "b/ANote/image/UI\347\254\224\350\256\260/2024-03-13-15-04-54.png" new file mode 100644 index 00000000..40babff5 --- /dev/null +++ "b/ANote/image/UI\347\254\224\350\256\260/2024-03-13-15-04-54.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:220ddb359887fcaa32919bf54370ca84fc9539f553ca377a53b5f5c4c21c50ae +size 686369 diff --git "a/ANote/image/UI\347\254\224\350\256\260/2024-03-13-15-58-27.png" "b/ANote/image/UI\347\254\224\350\256\260/2024-03-13-15-58-27.png" new file mode 100644 index 00000000..90a85e77 --- /dev/null +++ "b/ANote/image/UI\347\254\224\350\256\260/2024-03-13-15-58-27.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c97e8c9f67d206e62205754a3a580ebcdbe4b1ff93b1a4c188af26b02c4e6627 +size 188516 diff --git "a/ANote/image/Unity-URP\347\254\224\350\256\260/2ZhH9TmRF4YnbvM.png" "b/ANote/image/Unity-URP\347\254\224\350\256\260/2ZhH9TmRF4YnbvM.png" new file mode 100644 index 00000000..c2b63bde --- /dev/null +++ "b/ANote/image/Unity-URP\347\254\224\350\256\260/2ZhH9TmRF4YnbvM.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c20a2386e00a436d382c33b13aeb95bc2603670ab2ca6e51c84c27eb7fdd70b2 +size 61093 diff --git "a/ANote/image/Unity-URP\347\254\224\350\256\260/HcRCKSxZ2brWFNs.png" "b/ANote/image/Unity-URP\347\254\224\350\256\260/HcRCKSxZ2brWFNs.png" new file mode 100644 index 00000000..811ddfb9 --- /dev/null +++ "b/ANote/image/Unity-URP\347\254\224\350\256\260/HcRCKSxZ2brWFNs.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:34f48f9e4f4c3bc6e117ab83c3cbab7ecc7433c74bb6a3240f9c6780d9a62c9e +size 27978 diff --git "a/ANote/image/Unity-URP\347\254\224\350\256\260/QvCf5GZmbrxEcjW.png" "b/ANote/image/Unity-URP\347\254\224\350\256\260/QvCf5GZmbrxEcjW.png" new file mode 100644 index 00000000..3fc0ff39 --- /dev/null +++ "b/ANote/image/Unity-URP\347\254\224\350\256\260/QvCf5GZmbrxEcjW.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ec23ed3289a01b74a5ff42e8d8902a9246418ead6a455869fafafeb6f25f50b9 +size 55005 diff --git "a/ANote/image/Unity-URP\347\254\224\350\256\260/d3ACNrGKf984cqp.png" "b/ANote/image/Unity-URP\347\254\224\350\256\260/d3ACNrGKf984cqp.png" new file mode 100644 index 00000000..1b9a4a05 --- /dev/null +++ "b/ANote/image/Unity-URP\347\254\224\350\256\260/d3ACNrGKf984cqp.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4589292dcda6508af0e0767894be9f173d1b8aefb07084861c44e8cce2d21396 +size 165935 diff --git "a/ANote/image/Unity-URP\347\254\224\350\256\260/eJD95mkRaKYfXCI.png" "b/ANote/image/Unity-URP\347\254\224\350\256\260/eJD95mkRaKYfXCI.png" new file mode 100644 index 00000000..532ba8f1 --- /dev/null +++ "b/ANote/image/Unity-URP\347\254\224\350\256\260/eJD95mkRaKYfXCI.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:830a2c3d48f711f3b8ae46c2971c387fb290449d7ab7f4f238d6263dbe996aa0 +size 455094 diff --git "a/ANote/image/Unity-URP\347\254\224\350\256\260/gxXTIEYSa1s6GcA.png" "b/ANote/image/Unity-URP\347\254\224\350\256\260/gxXTIEYSa1s6GcA.png" new file mode 100644 index 00000000..15e49c04 --- /dev/null +++ "b/ANote/image/Unity-URP\347\254\224\350\256\260/gxXTIEYSa1s6GcA.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b7a71f740d96ca0b5e93b74895088ae3fa763356a74ea1675da2f0271e72ab8d +size 36624 diff --git "a/ANote/image/Unity-URP\347\254\224\350\256\260/xTS4cWCkB5mGjq3.png" "b/ANote/image/Unity-URP\347\254\224\350\256\260/xTS4cWCkB5mGjq3.png" new file mode 100644 index 00000000..66a40ce4 --- /dev/null +++ "b/ANote/image/Unity-URP\347\254\224\350\256\260/xTS4cWCkB5mGjq3.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8b8ca41009a4c12ccda77ff479cd9e088853b72e6d62a4989c4561ae568b4fa5 +size 55692 diff --git "a/ANote/image/Unity-URP\347\254\224\350\256\260/zSdjiUr5RY8DhxW.png" "b/ANote/image/Unity-URP\347\254\224\350\256\260/zSdjiUr5RY8DhxW.png" new file mode 100644 index 00000000..0f532ac0 --- /dev/null +++ "b/ANote/image/Unity-URP\347\254\224\350\256\260/zSdjiUr5RY8DhxW.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6bdf7a8e52506d769ea0cd9c689e04945b79a89a061a5312a408d401158ace87 +size 47982 diff --git "a/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-17-21-54-35.png" "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-17-21-54-35.png" new file mode 100644 index 00000000..19e4fc12 --- /dev/null +++ "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-17-21-54-35.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d11db722021a6ce0425d2dab464cf39d876b082adf3ba4001b55acd6dad4c38a +size 46592 diff --git "a/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-17-21-55-59.png" "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-17-21-55-59.png" new file mode 100644 index 00000000..3e6c9851 --- /dev/null +++ "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-17-21-55-59.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:dce3eeb8245f9f92e9c0d82afd6b71cbc2526a8322477df51f274e487e884ab8 +size 20808 diff --git "a/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-17-21-56-27.png" "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-17-21-56-27.png" new file mode 100644 index 00000000..d1b73f88 --- /dev/null +++ "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-17-21-56-27.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:286749e0d244018ab86454195a797b798093beb813f77395542e9d45f114203a +size 20440 diff --git "a/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-17-21-57-54.png" "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-17-21-57-54.png" new file mode 100644 index 00000000..8910ec92 --- /dev/null +++ "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-17-21-57-54.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:47b80b40b4520f10b1b16a00d34defcfc3c21518188864e33728fbe0de8c1df6 +size 78256 diff --git "a/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-18-17-43-57.png" "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-18-17-43-57.png" new file mode 100644 index 00000000..b9366a6d --- /dev/null +++ "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-18-17-43-57.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f9a75808dba730a6b21207f429e6fc867ada38a47108ceb93cca315d83536acf +size 126371 diff --git "a/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-18-17-44-40.png" "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-18-17-44-40.png" new file mode 100644 index 00000000..6f55cef8 --- /dev/null +++ "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-18-17-44-40.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b9348593996c20ea382fe40013e8402f909d8e46aa05c862e432c8eb8d6710d6 +size 11076 diff --git "a/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-19-15-21-59.png" "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-19-15-21-59.png" new file mode 100644 index 00000000..c5675277 --- /dev/null +++ "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-19-15-21-59.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e26dbe0a00ddf2f06459cb2decd38aa6023257eb16ae490b0ea145ca001d1825 +size 39861 diff --git "a/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-19-15-22-30.png" "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-19-15-22-30.png" new file mode 100644 index 00000000..fba55ac4 --- /dev/null +++ "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-19-15-22-30.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:03919293c84f0d3a474d5d8e057212abc3bce89414dd235d3f254a2ffedfc006 +size 6876 diff --git "a/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-19-16-15-01.png" "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-19-16-15-01.png" new file mode 100644 index 00000000..164fc7e4 --- /dev/null +++ "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-19-16-15-01.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:16879b7fbd8debc3f470a8008399f038054851771eaf21c8f37c6c4eac056602 +size 26502 diff --git "a/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-19-16-56-21.png" "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-19-16-56-21.png" new file mode 100644 index 00000000..bd7ca9c1 --- /dev/null +++ "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-19-16-56-21.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:37979f0afe9409839cad2c1a635779763f86601309706efa352697ded8a502a2 +size 23601 diff --git "a/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-19-17-00-03.png" "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-19-17-00-03.png" new file mode 100644 index 00000000..eda542cd --- /dev/null +++ "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-19-17-00-03.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fcf9b33f4fb1f656bcb863bd07d834558168e1803ff73174fea1f3ce9e6b0704 +size 102756 diff --git "a/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-20-11-14-53.png" "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-20-11-14-53.png" new file mode 100644 index 00000000..567fcf13 --- /dev/null +++ "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-20-11-14-53.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:edfc1abd0f0c7b56e3ddfbb0e1327bd05de6c2ddb12eb62e8cf72be0c52705cd +size 41784 diff --git "a/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-20-11-24-38.png" "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-20-11-24-38.png" new file mode 100644 index 00000000..092939a0 --- /dev/null +++ "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-20-11-24-38.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bcba3b0c85f2dde936053f9f61efeb91dc5d2874b15e6a7c829cf5aab0de0a0d +size 70855 diff --git "a/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-20-11-30-10.png" "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-20-11-30-10.png" new file mode 100644 index 00000000..33b7c19c --- /dev/null +++ "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-20-11-30-10.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f0410a3b5becb71c80a27e27b66002978fcdc4a8f1691eb9ec589b22d91fb2e3 +size 63282 diff --git "a/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-20-11-34-30.png" "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-20-11-34-30.png" new file mode 100644 index 00000000..a23af201 --- /dev/null +++ "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-20-11-34-30.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d537cb47ef7ddc9dae9f61120bb0f6b824912fef66a1d1847f5d6847589c07d3 +size 2911 diff --git "a/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-20-11-41-31.png" "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-20-11-41-31.png" new file mode 100644 index 00000000..ee71d775 --- /dev/null +++ "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-20-11-41-31.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f580a9e0c19ce3b8f069176948d5d26d3c261ae6085018b7099943562d986bb3 +size 143380 diff --git "a/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-20-11-48-28.png" "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-20-11-48-28.png" new file mode 100644 index 00000000..48dbf66a --- /dev/null +++ "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-20-11-48-28.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d06f461aab010de6258aad16f8aa4ac19fc269c3b7e2f6cda3ad165004e6f55f +size 27892 diff --git "a/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-20-11-51-05.png" "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-20-11-51-05.png" new file mode 100644 index 00000000..c8cacc80 --- /dev/null +++ "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-20-11-51-05.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:97a6a4bdcc0ebb4bc5ec95ad5e2929de4c5098f7f9b57dc82292a4b10f2fb4ca +size 113095 diff --git "a/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-20-11-52-38.png" "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-20-11-52-38.png" new file mode 100644 index 00000000..6475f8b4 --- /dev/null +++ "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-20-11-52-38.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:19921e348cd6618f2327919f0482036c2a969d6e0984bea951a468d48a7864c5 +size 103384 diff --git "a/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-20-11-53-24.png" "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-20-11-53-24.png" new file mode 100644 index 00000000..a97f9dd7 --- /dev/null +++ "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-20-11-53-24.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bf005ef2d0bf1637b5c44e6ad105ad95c5cec394f5afadac793f025cc71df628 +size 119712 diff --git "a/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-20-15-38-38.png" "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-20-15-38-38.png" new file mode 100644 index 00000000..26717f7c --- /dev/null +++ "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-20-15-38-38.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9c3df29e7eaf44e66582a952c5203950c8bbb3b2d43a957061a3f1a202c2b985 +size 9638 diff --git "a/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-20-15-39-33.png" "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-20-15-39-33.png" new file mode 100644 index 00000000..9f156180 --- /dev/null +++ "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-20-15-39-33.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:be0cc586b9bef8328de64e4f6522f8a46000d4da103d34ef7da598a13365a37b +size 17093 diff --git "a/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-20-15-41-33.png" "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-20-15-41-33.png" new file mode 100644 index 00000000..59329318 --- /dev/null +++ "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-20-15-41-33.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e21d51267928db14f6f498900778689c194b500cee455fa9e192acdac197a890 +size 17088 diff --git "a/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-20-15-41-49.png" "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-20-15-41-49.png" new file mode 100644 index 00000000..3177778c --- /dev/null +++ "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-20-15-41-49.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d16750c96b8abc00432ffb02dfd9cac3647d43213e015e7c4995102eda6d0ee0 +size 10424 diff --git "a/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-20-15-44-33.png" "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-20-15-44-33.png" new file mode 100644 index 00000000..dd1fd0d7 --- /dev/null +++ "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-20-15-44-33.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:707084b0226f4dfa47550d500813f35323ea21e03e8fc4adc65d6df3de534493 +size 5094 diff --git "a/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-20-16-10-03.png" "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-20-16-10-03.png" new file mode 100644 index 00000000..845d95a1 --- /dev/null +++ "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-20-16-10-03.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:12b799b51fbf3f53fe3655933ce3a78976f23a5d4d05e99c56dd0ff5d8881722 +size 9724 diff --git "a/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-20-16-34-32.png" "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-20-16-34-32.png" new file mode 100644 index 00000000..b9655cbe --- /dev/null +++ "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-20-16-34-32.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b88c53e954904eb8be39fb2b7c6782a0794cb44a61750e00a01b774f1e70fb5b +size 6609 diff --git "a/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-20-17-10-26.png" "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-20-17-10-26.png" new file mode 100644 index 00000000..3d7973ea --- /dev/null +++ "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-20-17-10-26.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:318f9ce7dc6bfaeb3452613d7c271e49706a0e4d374b13c359562de9370118f5 +size 11903 diff --git "a/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-20-17-33-07.png" "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-20-17-33-07.png" new file mode 100644 index 00000000..de5f11aa --- /dev/null +++ "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-20-17-33-07.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c993c32139a400c0b01f1b7806f29025cef5bf341dcc5e867641cbf867d0b70a +size 19408 diff --git "a/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-20-18-35-15.png" "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-20-18-35-15.png" new file mode 100644 index 00000000..fa79d0f9 --- /dev/null +++ "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-20-18-35-15.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:240981faa47e406c4fbbf74f572804bf23abbafc19f86d9cbe925f8c0d81f801 +size 10273 diff --git "a/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-21-10-24-42.png" "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-21-10-24-42.png" new file mode 100644 index 00000000..9af96e3f --- /dev/null +++ "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-21-10-24-42.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f2fd28545188e099bc21d1de0b32c031b199d110921b161e5b109f4a25d8e1ed +size 594289 diff --git "a/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-21-10-27-01.png" "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-21-10-27-01.png" new file mode 100644 index 00000000..bdfbe53b --- /dev/null +++ "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-21-10-27-01.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:dfa80ac9348ead2db081ed430d245d5df5d0ab8bd22d21b9c138fdd0fb77c64c +size 176578 diff --git "a/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-21-10-29-35.png" "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-21-10-29-35.png" new file mode 100644 index 00000000..e300d28f --- /dev/null +++ "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-21-10-29-35.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d8af81d29bc33a1c28858297d1646bc4c2e31a5e48857c0268100635153c21a5 +size 290552 diff --git "a/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-21-10-32-09.png" "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-21-10-32-09.png" new file mode 100644 index 00000000..570fe64e --- /dev/null +++ "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-21-10-32-09.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e3aedf9496e4e2e2f82d9960fb480bc7445aa00f0f1172ba1eb04f5dc9ce8422 +size 183456 diff --git "a/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-21-10-42-53.png" "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-21-10-42-53.png" new file mode 100644 index 00000000..c8722719 --- /dev/null +++ "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-21-10-42-53.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e1d5e932945b8275b82a538889ec15fdac25847e94271b80667bf604e18e6c66 +size 1967 diff --git "a/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-21-10-55-02.png" "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-21-10-55-02.png" new file mode 100644 index 00000000..ad037b5c --- /dev/null +++ "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-21-10-55-02.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8befc6480654d64c00b05d2e1d8a8bfd36987734ab6c1cfd788040c5324c92e5 +size 142051 diff --git "a/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-21-10-56-27.png" "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-21-10-56-27.png" new file mode 100644 index 00000000..85476f25 --- /dev/null +++ "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-21-10-56-27.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c463cbdad3fead90d40b28cdc6c7f77e6ac569f0f89dcc3febc035374c5afbc0 +size 139952 diff --git "a/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-21-11-07-54.png" "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-21-11-07-54.png" new file mode 100644 index 00000000..a7f34fc4 --- /dev/null +++ "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-21-11-07-54.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:411cdd6082d99b6ede824133a5b1d9fe46068ff371776f7250db41c976bbe99c +size 33077 diff --git "a/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-21-11-12-10.png" "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-21-11-12-10.png" new file mode 100644 index 00000000..4ffa7879 --- /dev/null +++ "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-21-11-12-10.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:721f86e2ac8ec0fd4747f873ad3ffa29b437c2659ac494d38375ea966e9f30ba +size 45666 diff --git "a/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-21-11-14-07.png" "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-21-11-14-07.png" new file mode 100644 index 00000000..80af40df --- /dev/null +++ "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-21-11-14-07.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:edc5b6ff322d4429df6e8995c79a3de66fdd4ff10b2a256b0ae36fda1061b9fb +size 41940 diff --git "a/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-21-11-24-01.png" "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-21-11-24-01.png" new file mode 100644 index 00000000..a4e5e058 --- /dev/null +++ "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-21-11-24-01.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2cf5edc21aa896b09e22b407de86288b5d47e89e7ee512723f0e6fc00364cc3a +size 69832 diff --git "a/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-21-11-30-37.png" "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-21-11-30-37.png" new file mode 100644 index 00000000..a6654989 --- /dev/null +++ "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-21-11-30-37.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a09c2995a13c1c557f47c7ed1945b86f72a5ef089cc1be643375006ab99617bf +size 451024 diff --git "a/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-24-14-05-37.png" "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-24-14-05-37.png" new file mode 100644 index 00000000..031a3969 --- /dev/null +++ "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-24-14-05-37.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:54d0b0a0cd31500e0cea09d0677fb2084b9cf755b8058d3e60b46fe3cb48f486 +size 25537 diff --git "a/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-24-14-23-38.png" "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-24-14-23-38.png" new file mode 100644 index 00000000..971cda19 --- /dev/null +++ "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-24-14-23-38.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fcffb748bc48dec6f80b8d4596f165fc9725afe3e00c596454b63ac3a4a99cd6 +size 30726 diff --git "a/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-24-20-27-28.png" "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-24-20-27-28.png" new file mode 100644 index 00000000..21e5689a --- /dev/null +++ "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-24-20-27-28.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7893a32efbaa7ab32e8f6b82a753051a62b79bb96aa12e4579cac3eb9f2da8e8 +size 17894 diff --git "a/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-24-20-33-18.png" "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-24-20-33-18.png" new file mode 100644 index 00000000..f41806fa --- /dev/null +++ "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-24-20-33-18.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d48776611968c26285ccc019a5874d0436ed96ba576e68474a9dbd7cb3021bd9 +size 15415 diff --git "a/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-24-21-54-50.png" "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-24-21-54-50.png" new file mode 100644 index 00000000..1bc59e5a --- /dev/null +++ "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-24-21-54-50.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:73a8433b184eea1bb453703c818d44cc585e2f486c80aaf97b9e620344cfaa44 +size 40688 diff --git "a/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-26-17-18-18.png" "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-26-17-18-18.png" new file mode 100644 index 00000000..26b49c82 --- /dev/null +++ "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-26-17-18-18.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f023df74cce14473abff5f9138887bc26c9f316ba61c58b0fe7965617a7b02c4 +size 27248 diff --git "a/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-27-10-17-42.png" "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-27-10-17-42.png" new file mode 100644 index 00000000..023f81f0 --- /dev/null +++ "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-27-10-17-42.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a045646ef50e0bea0457f079761b918e4d7b550cdbeb2608afca7f0575032733 +size 15634 diff --git "a/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-27-18-24-08.png" "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-27-18-24-08.png" new file mode 100644 index 00000000..87adc997 --- /dev/null +++ "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-27-18-24-08.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9b84c9005bd3c24a36f42d43e49849640f615d04905ad1f68261057cb262983c +size 6370 diff --git "a/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-27-18-26-27.png" "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-27-18-26-27.png" new file mode 100644 index 00000000..a4d36988 --- /dev/null +++ "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-27-18-26-27.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c357dd3a283a3d1692b19ea09e5c5240c31f00bf0b7b87b4b56a7aa9e8e8012a +size 76322 diff --git "a/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-27-18-39-42.png" "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-27-18-39-42.png" new file mode 100644 index 00000000..693389ad --- /dev/null +++ "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-27-18-39-42.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d7c0978078e54f4bae9d443352c50c3b23dcfdfbc3f91301d509e6c231755c2c +size 2976 diff --git "a/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-27-18-46-29.png" "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-27-18-46-29.png" new file mode 100644 index 00000000..6a7e9e2a --- /dev/null +++ "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-27-18-46-29.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fed296399257ad1e351ae78242d2bc6c6538e788b5afc8e5e65c631ade031e27 +size 6694 diff --git "a/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-27-18-47-30.png" "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-27-18-47-30.png" new file mode 100644 index 00000000..f642b3bb --- /dev/null +++ "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-27-18-47-30.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8b6eb5a2929be784d8e877df4b6f61ee33cc03df2b52bdcd7378375f13cfa1c0 +size 20002 diff --git "a/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-27-18-48-01.png" "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-27-18-48-01.png" new file mode 100644 index 00000000..21bc1d89 --- /dev/null +++ "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-27-18-48-01.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:847c69de4fe863af433c3e038f1f8ab6f01c325a71c2f009603751d8c044f5f2 +size 16695 diff --git "a/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-27-18-51-53.png" "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-27-18-51-53.png" new file mode 100644 index 00000000..4402907c --- /dev/null +++ "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-01-27-18-51-53.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:22eab0741a4d1eae4db56723f6fc0e650deb5385aac52c898f1fb4ac15699a9b +size 22149 diff --git "a/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-03-07-11-15-01.png" "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-03-07-11-15-01.png" new file mode 100644 index 00000000..0b12e295 --- /dev/null +++ "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-03-07-11-15-01.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:430f90a7a53fc339dbb619d98150a37df3db1c39b514599de40dc6ce5368d404 +size 43801 diff --git "a/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-03-07-11-21-04.png" "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-03-07-11-21-04.png" new file mode 100644 index 00000000..ff6ebbd2 --- /dev/null +++ "b/ANote/image/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/2022-03-07-11-21-04.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8862015b2be945c65b8d7a91280a09c025370dcb9c80e77d215963809fb93268 +size 36844 diff --git "a/ANote/image/\346\223\215\344\275\234\347\263\273\347\273\237\347\254\224\350\256\260/2022-02-13-16-52-59.png" "b/ANote/image/\346\223\215\344\275\234\347\263\273\347\273\237\347\254\224\350\256\260/2022-02-13-16-52-59.png" new file mode 100644 index 00000000..97b4dfca --- /dev/null +++ "b/ANote/image/\346\223\215\344\275\234\347\263\273\347\273\237\347\254\224\350\256\260/2022-02-13-16-52-59.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:98e0e9195cf2338103638014f64d144a038f7dd713499c66ee290471bea7e5a2 +size 328311 diff --git "a/ANote/image/\346\227\245\345\270\270\347\254\224\350\256\260/2024-02-22-18-15-42.png" "b/ANote/image/\346\227\245\345\270\270\347\254\224\350\256\260/2024-02-22-18-15-42.png" new file mode 100644 index 00000000..34a92dc1 --- /dev/null +++ "b/ANote/image/\346\227\245\345\270\270\347\254\224\350\256\260/2024-02-22-18-15-42.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:372c80d8417a227dddebe2fc5ca2fe8190b9d8c380d49bdf62e8aabb1e3f1051 +size 47220 diff --git "a/ANote/image/\346\227\245\345\270\270\347\254\224\350\256\260/2024-02-22-18-16-56.png" "b/ANote/image/\346\227\245\345\270\270\347\254\224\350\256\260/2024-02-22-18-16-56.png" new file mode 100644 index 00000000..c7788f52 --- /dev/null +++ "b/ANote/image/\346\227\245\345\270\270\347\254\224\350\256\260/2024-02-22-18-16-56.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:67a4aa5aacd447af5ba5651d0918e80c06fb35e49170b448ff895f13109a49de +size 116066 diff --git "a/ANote/image/\346\227\245\345\270\270\347\254\224\350\256\260/2024-02-22-18-19-36.png" "b/ANote/image/\346\227\245\345\270\270\347\254\224\350\256\260/2024-02-22-18-19-36.png" new file mode 100644 index 00000000..1ef09875 --- /dev/null +++ "b/ANote/image/\346\227\245\345\270\270\347\254\224\350\256\260/2024-02-22-18-19-36.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:64d31438bf45a9e44ca74b9b8534dd3a4681cfd484f6e916b73652c26727badb +size 81075 diff --git "a/ANote/image/\346\227\245\345\270\270\347\254\224\350\256\260/2024-02-22-18-22-24.png" "b/ANote/image/\346\227\245\345\270\270\347\254\224\350\256\260/2024-02-22-18-22-24.png" new file mode 100644 index 00000000..2032c3b9 --- /dev/null +++ "b/ANote/image/\346\227\245\345\270\270\347\254\224\350\256\260/2024-02-22-18-22-24.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:32871cab94a385d7957a16375a6a53e71140052eb702e7b911347d01e52714ed +size 441016 diff --git "a/ANote/image/\346\227\245\345\270\270\347\254\224\350\256\260/2024-02-28-19-49-34.png" "b/ANote/image/\346\227\245\345\270\270\347\254\224\350\256\260/2024-02-28-19-49-34.png" new file mode 100644 index 00000000..f2ee05a4 --- /dev/null +++ "b/ANote/image/\346\227\245\345\270\270\347\254\224\350\256\260/2024-02-28-19-49-34.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4fba126922188ba698b068a89ac75ede298840ed303abac1e919dec1e737a36f +size 103487 diff --git "a/ANote/image/\346\227\245\345\270\270\347\254\224\350\256\260/2024-02-28-19-54-28.png" "b/ANote/image/\346\227\245\345\270\270\347\254\224\350\256\260/2024-02-28-19-54-28.png" new file mode 100644 index 00000000..4d084767 --- /dev/null +++ "b/ANote/image/\346\227\245\345\270\270\347\254\224\350\256\260/2024-02-28-19-54-28.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b05d92ea9ba0d52660f15d03c007b5f060003f75f33b7424bff055e951cfb214 +size 10697 diff --git "a/ANote/image/\346\227\245\345\270\270\347\254\224\350\256\260/2024-02-28-19-55-57.png" "b/ANote/image/\346\227\245\345\270\270\347\254\224\350\256\260/2024-02-28-19-55-57.png" new file mode 100644 index 00000000..635bd6f3 --- /dev/null +++ "b/ANote/image/\346\227\245\345\270\270\347\254\224\350\256\260/2024-02-28-19-55-57.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0ae3c5e7e576de5d8353d61d517072df6b5d4609f5c212f744ed862c28d5eb18 +size 10559 diff --git "a/ANote/image/\346\227\245\345\270\270\347\254\224\350\256\260/2024-02-28-19-57-33.png" "b/ANote/image/\346\227\245\345\270\270\347\254\224\350\256\260/2024-02-28-19-57-33.png" new file mode 100644 index 00000000..c2b802bc --- /dev/null +++ "b/ANote/image/\346\227\245\345\270\270\347\254\224\350\256\260/2024-02-28-19-57-33.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c50c39bd5d6feeb42820326a2f1ec1076ce6ca9d3387f487c8d134808eb66e04 +size 35888 diff --git a/ANote/index.html b/ANote/index.html new file mode 100644 index 00000000..34a3d34e --- /dev/null +++ b/ANote/index.html @@ -0,0 +1,8494 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ANote - 王国庆的博客 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + 跳转至 + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + + + + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git "a/ANote/jenkins\350\256\260\345\275\225/index.html" "b/ANote/jenkins\350\256\260\345\275\225/index.html" new file mode 100644 index 00000000..f17e79a8 --- /dev/null +++ "b/ANote/jenkins\350\256\260\345\275\225/index.html" @@ -0,0 +1,8426 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + jenkins记录 - 王国庆的博客 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + 跳转至 + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+ +
+ + + + + + + + + +

jenkins记录

+

mac 安装目录:/usr/local/Cellar/jenkins-lts/2.235.5/bin

+
+
+ LabRat: Place arrows on the board to route the most mice into your home base.
Click here for details
+ +
    +
  • Mice spawn at frequent, randomized intervals from locations randomly chosen at startup.
  • +
  • The board has holes at random locations, both mice and cats can fall into those holes and despawn when that happens.
  • +
  • At start of play, walls are randomly placed between grid cells.
  • +
  • Cats and mice travel on the grid, changing direction when they hit a wall or travel over an arrow.
  • +
  • Cats spawn in random squares. When a cat and mouse intersect, the mouse is eaten.
  • +
  • When a mouse hits a player's 'home base' (one of the four dots placed near the center of the grid), the mouse disappears, and the player is awarded a point.
  • +
  • Similarly, when a cat hits a player's 'home base', the cat disappears, and the player gets negative points.
  • +
  • Players can place arrows in cells of the board. The green player places green arrows, the red player places red arrows, etc. A player cannot place their arrows in a cell occupied by an arrow of another player.
  • +
  • Once a player has three arrows on the board, their next placed arrow removes their oldest arrow on the board.
  • +
  • Only one player is human. The AI players just place their arrows randomly at random intervals.
  • +
  • At the end of 30 seconds, the player with the most points wins.
  • +
  • Keyboard controls allow the user to reset the simulation.
  • +
  • Walls can be added at runtime by left clicking while holding the shift key down.
  • +
+
+

Lab Rat

+
+

说明:原文引用的本地资源 image/jenkins记录/LabRat.gif 当前不在仓库中,暂以占位图保留阅读结构,后续如找回原 GIF 可直接替换。

+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git "a/ANote/shader\350\256\260\345\275\225/index.html" "b/ANote/shader\350\256\260\345\275\225/index.html" new file mode 100644 index 00000000..921ecac7 --- /dev/null +++ "b/ANote/shader\350\256\260\345\275\225/index.html" @@ -0,0 +1,8546 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + shader记录 - 王国庆的博客 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + 跳转至 + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+ +
+ + + + + + + + + +

shader记录

+

如何开始用 C++ 写一个光栅化渲染器?

+

bilibili的 GAMES101-现代计算机图形学入门-闫令琪

+

庄懂-BoyanTata +- [ ] 第二节没有完成 (屏幕空间使用,及素描风格)

+

庄懂公开课资源

+

学习课程中的问题

+
    +
  • +

    [ ] z缓冲消隐算法 (投影矩阵提到)

    +
  • +
  • +

    [ ] Mapcap昆虫最后的颜色为啥是相乘和相加?(第一章第四节)

    +
  • +
  • +

    [ ] 视差贴图,视差映射 ,视差偏移没有仔细学习(第二章第二节)。

    +
  • +
  • +

    [ ] 绕y轴旋转的旋转矩阵怎么写是对的,需要和unity Transform旋转一致(第三章第一节 Cubemap旋转) + >https://wgqing.com/unity%E7%9F%A9%E9%98%B5%E5%8F%98%E6%8D%A2%E4%BB%8E%E6%A8%A1%E5%9E%8B%E7%A9%BA%E9%97%B4%E5%88%B0%E5%B1%8F%E5%B9%95%E7%A9%BA%E9%97%B4%E7%9A%84%E8%BD%AC%E6%8D%A2/

    +
  • +
  • +

    [ ] 次表面散射,Skin_LUT (第四章第二节),皮肤阴影区域的SSS效果调整不理想

    +
  • +
  • [ ] 屏幕后处理,添加玻璃破碎效果,屏幕适配失败,不同分辨率时破碎效果不能保持居中(第五章第一节第二课题,BrokenGlass)
  • +
  • [ ] 火焰形状扰动后输出异常 (第六章第一节)
  • +
  • [ ] 动画纹理(VAT),(第六章第三节),(需要Hodini)
  • +
  • [ ] FlowMap的意义,案例中使用FlowMap没有看出效果在哪,(第六章第三节)
  • +
  • [ ] 宝石自发光,光晕效果(第八章)
  • +
+

水体渲染

+
    +
  1. 水体颜色
      +
    • 根据水底的深度计算(获取深度图)区分浅水区和深水区得到颜色
    • +
    +
  2. +
  3. 反射
      +
    • 根据PlanarReflection获取 _ReflectionTex纹理,然后使用屏幕空间作为UV进行采样(使用水的法线贴图进行扰动uv),得到反射颜色;增加菲涅尔效果
    • +
    +
  4. +
  5. 折射/水底
      +
    • 使用grabPass获取屏幕颜色,然后使用屏幕空间作为UV进行采样(使用水的法线贴图进行扰动UV),得到折射的颜色
    • +
    +
  6. +
  7. 焦散
      +
    • 使用深度还原的世界坐标作为UV采样焦散纹理,采样两次取两次的最小值 (应该可以使用默认UV进行采样)
    • +
    +
  8. +
  9. 岸边泡沫
      +
    • 使用根据time使用sin波向岸边推送波浪模拟的泡沫,使用一个noise进行扰动,需要处理遮罩 保证只在岸边有泡沫
    • +
    +
  10. +
  11. 波浪(Wave)(顶点动画)
      +
    • 正弦/余弦
    • +
    • 格斯特纳波(Gerstner)
    • +
    +
  12. +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git "a/ANote/socket\346\265\213\350\257\225\347\273\223\346\236\234/index.html" "b/ANote/socket\346\265\213\350\257\225\347\273\223\346\236\234/index.html" new file mode 100644 index 00000000..ef98780c --- /dev/null +++ "b/ANote/socket\346\265\213\350\257\225\347\273\223\346\236\234/index.html" @@ -0,0 +1,8417 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + socket测试结果 - 王国庆的博客 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + 跳转至 + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+ +
+ + + + + + + + + +

socket测试结果

+

socket测试结果(基于发送和接收缓冲区没有缓存的情况下测试,有缓存也一样) +SocketOptionName.Linger=false 且没有异步或同步监听接受接口,close 后发送Fin包(close没有延时)(LingerState参数无影响) +SocketOptionName.Linger=false 且有异步或同步监听接受接口,close 后发送Rst包(close没有延时)(LingerState参数无影响)(socket的都关闭了还接收数据肯定错误)

+

SocketOptionName.Linger=false 且没有异步或同步监听接受接口,close 后发送Fin包(close延时1秒) +SocketOptionName.Linger=false 且有异步或同步监听接受接口,close 后发送Fin包(close延时1秒)

+

SocketOptionName.Linger=true 且没有异步或同步监听接受接口,close 后发送Rst包(close没有延时) +SocketOptionName.Linger=true 且有异步或同步监听接受接口,close 后发送Rst包(close没有延时)

+

SocketOptionName.Linger=true 且没有异步或同步监听接受接口,close 后发送Fin包(close延时1秒) +SocketOptionName.Linger=true 且有异步或同步监听接受接口,close 后发送Fin包(close延时1秒)

+

Socket.Shutdown(SocketShutdown.Receive); 只是通知内核缓冲区接收消息的流,我不收数据了,不给对端任何消息。 +直到对端发消息过来后才给对端发送Rst包告诉他网络重置(出错了)。 (自己这个时候在发送数据提示shutdown错误) +Socket.Shutdown(SocketShutdown.Send)只是关闭了内核缓冲区发送消息的流 给对端发送Fin包 受到ack包后会受到0字节长度的消息。 +(对端收到0长度消息后如果继续Receive消息:内核自己会给用户态返回0长度消息) +shutdown 只是关闭网络连接 并没有关闭socket文件描述符,所以还得用close()关闭文件描述符

+

大端小端 +如果自己设置RecvBuffSize=0 网卡接收数据会另外开辟一块缓冲区存放数据直到用户态调用Receive返回给用户

+

jit(动态编译)和aot(静态编译)

+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git "a/ANote/unity\347\274\226\350\276\221\345\231\250\347\254\224\350\256\260/index.html" "b/ANote/unity\347\274\226\350\276\221\345\231\250\347\254\224\350\256\260/index.html" new file mode 100644 index 00000000..33d7276a --- /dev/null +++ "b/ANote/unity\347\274\226\350\276\221\345\231\250\347\254\224\350\256\260/index.html" @@ -0,0 +1,8478 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + unity编辑器笔记 - 王国庆的博客 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + 跳转至 + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+ +
+ + + + + + + + + +

unity编辑器笔记

+

Problem

+

Odin编辑器插件

+
+

Odin 收费 +Odin - Inspector and Serializer

+

Unity3D研究院编辑器之不影响原有布局拓展Inspector(二十四)
+https://gist.github.com/liortal53/352fda2d01d339306e03
+拓展RectTransform等 +

+

Unity3D编辑器扩展

+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git "a/ANote/web\345\274\200\345\217\221\347\254\224\350\256\260.md/index.html" "b/ANote/web\345\274\200\345\217\221\347\254\224\350\256\260.md/index.html" new file mode 100644 index 00000000..a68f1e3c --- /dev/null +++ "b/ANote/web\345\274\200\345\217\221\347\254\224\350\256\260.md/index.html" @@ -0,0 +1,8630 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + web开发笔记 - 王国庆的博客 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + 跳转至 + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+ +
+ + + + + + + + + +

web开发笔记.md

+ +
请求网址: http://10.24.0.24:8889/executeCmd
+请求方法: POST
+状态代码: 200 
+远程地址: 10.24.0.24:8889
+引荐来源网址政策: strict-origin-when-cross-origin
+
+响应Header(response headers)
+Connection: keep-alive
+Content-Length: 6
+Content-Type: text/html;charset=utf-8
+Date: Wed, 30 Mar 2022 08:22:51 GMT
+Keep-Alive: timeout=60
+
+请求Header(request header)
+Accept: */*
+Accept-Encoding: gzip, deflate
+Accept-Language: zh-CN,zh;q=0.9
+Connection: keep-alive
+Content-Length: 56
+Content-Type: application/json
+Host: 10.24.0.24:8889
+Origin: http://10.24.0.24:8889
+Referer: http://10.24.0.24:8889/cmd
+User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.82 Safari/537.36
+X-Requested-With: XMLHttpRequest
+
+

request header

+
+

Accept-Language: 支持的语言类型,类似于一个数组,每种语言对应一个权重(0-1的数字),一般用来做多语言用

+

Content-Type: 请求体的类型
+设置响应内容的类型和编码, 经常配合mime模块使用

+
+
// 常用几种类型
+
+application/x-www-form-urlencoded  // a=1&b=2
+application/json // {"a": "1", "b": "2"}
+multipart/form-data //文件类型
+
+application/x-www-form-urlencoded;charset=utf-8
+
+
+

Referer: 资源在哪个网站中被使用,来源。可篡改,重要的东西不可依赖于他

+

Accept-Encoding: 浏览器支持的格式

+

Origin: 当前网站的来源,只支持post请求

+

Cookie: 每次发请求,都会带到服务端

+

If-Modified-Since: 缓存相关,详见下文的 Last-Modified

+

If-None-Match: 缓存相关,详见下文的 Etag

+

Range: Range:bytes=n-m, 请求内容的第n 到m 的字节

+
+
服务端设置res.setHeader('Content-Range', 'bytes n-m/total'),
+并设置状态码res.statusCode = 206
+
+

response header

+
+

Content-Type: 设置响应内容的类型和编码, 经常配合mime模块使用

+
+
res.setHeader('Content-Type', mime.getType(req.url) + ';charset=utf-8' )
+
+
+

Cache-Control: 强制缓存,设置缓存有效时长(比expires靠谱)

+
+
res.setHeader('Cache-Control', 'no-cache') //不设置强制缓存
+res.setHeader('Cache-Control', 'max-age=3600') //强制缓存1小时, 单位:秒
+
+
+

Expires: 强制缓存 单位:毫秒,设置缓存失效的一个时间点

+
+
res.setHeader('Expires', new Date( Date.now() + 3600 * 1000 ).toLocaleString()) 
+
+
+

Last-Modified: 上次修改时间

+
+
// 通过fs.stat 拿到 statRes, 拿到上次修改的时间
+// 设置给Last-Modified, 浏览器下次访问这个文件时候
+// 会带上一个头If-Modified-Since, 取出值old
+// 再次通过fs.stat获取修改时间,和old进行比较
+// 若相同,则设置304,并end,否则重新设置Last-Modified并重新返回文件
+res.setHeader('Last-Modified', statRes.ctime.toGMTString())
+
+
+

Etag: 存放根据文件内容计算出的价签字符串

+
+
// sign的计算可以根据实际情况来定,可以用md5文件内容加签得到一个值,
+// 常用的是文件内容的 修改时间的16进制+内容长度的16进制值,
+// 也可以通过其他算法, 得到一个不规则字符串,
+// 设置到Etag以后,浏览器下次访问这个文件,会带上一个头If-None-Match
+// 取到值old以后,重新根据文件内容用相同算法计算一个sign,和old比较,
+// 若相同,则设置304,并end,否则重新设置Etag并重新返回文件
+res.setHeader('Etag', sign);
+
+
+

3种缓存可以同时使用, Last-Modified和Etag是并且关系,有一个没对上,就重新发送文件

+
+

Content-Range: 返回部分内容,和响应头Range配合使用

+

Content-Length: 响应内容的字节长度

+

Access-Control-Allow-Origin: 允许哪个源来访问

+
+
可以设置成 *(所有网站) ,也可以设置一个白名单,当当前访问的域名在白名单中,
+就设置成当前访问的域名
+
+
+

Access-Control-Allow-Methods: 允许哪些方法访问,GET,POST,PUT,DELETE。。。

+

Access-Control-Allow-Credentials:是否可携带cookie
+需要前端ajax也设置xhr.withCredentials = true;

+

Content-Encoding: 告诉浏览器,按照那种方式解析文件

+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git "a/ANote/\345\205\266\344\273\226\350\256\260\345\275\225/index.html" "b/ANote/\345\205\266\344\273\226\350\256\260\345\275\225/index.html" new file mode 100644 index 00000000..e463ca49 --- /dev/null +++ "b/ANote/\345\205\266\344\273\226\350\256\260\345\275\225/index.html" @@ -0,0 +1,8403 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 其他记录 - 王国庆的博客 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + 跳转至 + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+ + +
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git "a/ANote/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/index.html" "b/ANote/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/index.html" new file mode 100644 index 00000000..9a38570d --- /dev/null +++ "b/ANote/\345\214\205\344\275\223\345\244\247\345\260\217\344\274\230\345\214\226\347\254\224\350\256\260/index.html" @@ -0,0 +1,9080 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 包体大小优化笔记 - 王国庆的博客 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + 跳转至 + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ +
+ + + + + + + + + +

包体大小优化笔记

+ + +
+

一些记录

+
    +
  • bsdiff差分算法
  • +
  • 出正式包怎么剥离无用的dll,比如log插件dll,Debug库,绘制插件dll(仅dev使用)参考
      +
    • 这里可以在打包正式包时将dll删除后打包
    • +
    +
  • +
+
+ + +

AssetBundle大小管理策略

+

主要问题:纹理,Shader,FBX动画

+ +

图集、图片文件过大

+

+

+使用第三方无损压缩,减小文件大小,这里文件大小是原来的1/3。

+

使用图片无损压缩打包测试,图片属性:RGBA 2048*2048,使用RGBA Crunched ETC2内存压缩 +| |压缩尺寸|压缩前|压缩后 +|-|-|-|-| +|图片文件||1564kb|481kb| +|AB文件|2048|268kb|260kb| +|AB文件|1024|96kb|89kb| +可以发现压缩后的图片打包AB文件比压缩前小大概7kb左右

+
    +
  • 图片在内存中没有压缩
    +

    纹理的压缩格式影响AB包的大小 (Android平台测试)
    +使用ETC2压缩生成AB包比使用 Crunched ETC生成AB包大3倍(这里只是说明压缩格式影响AB包文件大小,不说明压缩格式和包体的具体关系)。

    +
    +
  • +
+

Android平台

+

+

使用ETC 4Bit压缩后

+

+

使用 Crunched ETC 压缩(需要真机测试性能及是否支持该压缩格式),这个格式消耗cpu,进入GPU处理阶段会解压成etc压缩,GPU本身不支持 Crunched ETC压缩
+

+
+

推荐 Android 和IOS都用ASTC;
+参考:ASTC纹理压缩格式详解

+
+ +

FBX文件过大

+
+


+主要是animation和纹理占用比较大,每个带动画的角色都是这样的情况 +

+
+ +

使用R220002@ui_comeout.fbx动画文件测试

+

现在的FBX配置
+ +
+文件大小是4322kb,打包AB文件大小是1553kb

+

修改FBX配置(没有勾选 Resample Curves) +
+
+文件大小是4322kb,打包AB文件大小是499kb (这里fbx文件本身大小没变,untiy不修改源文件)
+
+修改后的内存大小是原来的1/4,Ab文件是原来的1/3 (动画表现一致)

+

使用脚本剔除无用scale数据及修改数据精度(没有勾选 Resample Curves) +这种方式每次animation导入修改后都不会保持,因为untiy默认重新导入animation并计算数据,需要在animation后处理中处理,每次变化都要执行一遍
+
+
+文件大小是4322kb,打包AB文件大小是310kb
+修改后内存更低了和原始比是原来的不到1/4,AB文件是原来的1/5 (这里fbx文件本身大小没变,untiy不修改源文件) (动画表现一致)

+

如果所有的角色动画都进行调整,包体可以明显减小

+

fbx导入到Unity之后是不会变化的,就是说Unity里面的所有编辑都不会保存在FBX文件里,可能在meta文件中,这里没有测试。

+

注意压缩后需要真机测试看看动画是否一致,这里是win editor测试的,没有真机测试

+
+

参考:
+https://blog.uwa4d.com/archives/Optimization_Animation.html
+https://zhuanlan.zhihu.com/p/353402448
+https://www.bzetu.com/344/.html
+https://blog.uwa4d.com/archives/UWA_Pipeline22.html

+

处理Animation时遇到的问题:
+
+
+一个fbx上出现两个animation 动画,__preview_Take 001应该是美术没有删除干净 +打包AB文件没有发现__preview_Take 001资源打包进去

+
+ + +

AB加载和卸载策略

+
+

现有加载策略: +1. 根据manifest配置文件进行加载资源并且找到依赖进行加载 +2. 只有同步加载没有异步加载 +3. 加载的Bundle和LoadAsset资源都做了缓存,但是没有引用计数

+

现有资源卸载策略:
+1. lua Bundle加载资源后直接卸载bundle; +1. 场景加载后将bundle卸载,在切换场景时: + - 执行LuaGC处理 + - 清理所有Asset的引用(将缓存的Dict清空)(资源泄露:bundle没有卸载,清空了asset引用,再次加载资源时会再次LoadAsset,会存在多份相同asset;加载效率利用率低,没有区分常驻内存资源和非常驻内存资源,bundle管理粗放) + +1. 非场景资源加载后没有卸载流程,全部依靠场景卸载时清理 +1. 除了上述bundle卸载之外,游戏运行中没有卸载bundle,内存占用过高容易崩溃 +1. 业务层只管加载,不处理卸载 +1. Resources.UnloadUnusedAssets()没有调用(场景加载时会自动调用,记不清了)

+
+

调整方案: +1. 同步和异步加载 +2. 使用引用计数方案对Bundle和Asset管理 +2. 增加图集管理(图集制作细分方案待定) +2. 使用加载和卸载成对管理,或者做内置管理和实例对象绑定

+

URP Package 内置shader打包问题

+
    +
  • +

    Universal Render Pipeline/Lit has too many Shader variants(150994944) +
    +URP 自带Lit Shader变体太多问题

    +
  • +
  • +

    Assertion failed on expression:`m_UserPathRemap.count(pathStr) == 0` +

    +
  • +
+

可能因为在Project Setting-->Graphing设置All include 然后打包又打进去所以报错了

+

包体优化结果

+

+左侧蓝色是优化后的,右侧红色是优化前的

+

DisableWriteTypeTree问题

+

关闭Bundle文件的type information数据写入,这是为了使用Unity版本不同做的兼容(标记数据,unity版本相同无用)

+
//BuildAssetBundleOptions.DisableWriteTypeTree
+BuildPipeline.BuildAssetBundles(dir,ls.ToArray(),BuildAssetBundleOptions.DeterministicAssetBundle | BuildAssetBundleOptions.ChunkBasedCompression|BuildAssetBundleOptions.DisableWriteTypeTree, BuildTarget.Android);
+
+

+红色是开启默认type information写入,asset classes就是写入的数据,左侧蓝色是关闭type information写入的大小 +bundle文件大小差5k多(每个bundle文件内容不一样,大小也不一样),还是很可观的

+
+

参考:https://blog.csdn.net/kangluo1/article/details/119089200

+
+

Shader变体处理

+
    +
  • +

    Graphics APIs 只保留OpenGLEL3就可以了,否则打包Shader时每个平台生成一套代码导致AB包过大 +

    +
  • +
  • +

    Graphics -> Tier Settings默认三种配置,每个shader会生成对应三套代码,这里将Tier三种配置设置成相同的,则Shader只会生成一套代码,减小包体

    +
  • +
+

+
    +
  • 单独打包Shader文件(没有材质球) 关于#pragma multi_compile#pragma shader_feature测试
  • +
+
            #pragma multi_compile  COM_M COM_N
+            #pragma multi_compile  COM_X COM_Y COM_Z
+            #pragma shader_feature _ A B
+            #pragma shader_feature E F
+
+

打包的AB包中的Shader keywords数量
+ +multi_compile两行的keywords组合相乘是最后的shader代码的数量,注意数量
+这里#pragma shader_feature 如果第一项是_则认为没有keywords,如果不是_,则默认打包进第一个选项(这里是E

+
    +
  • 单独打包材质球(将材质球和shader打包一起)
  • +
+
            #pragma multi_compile  COM_M COM_N
+            #pragma multi_compile  COM_X COM_Y COM_Z
+            #pragma shader_feature _ A B
+            #pragma shader_feature E F
+
+

打包的AB包中的Shader keywords数量

+

材质球keywords默认: +
+

+

材质球指定keywords后
+
+
+指定keywords后等于选择shader_feature的变体,multi_compile进行全部组合

+

总结:材质球指定shader_feature keywords后和shader打包到AB文件中,shader的keywords是受材质球的keywords配置影响的;加入了GraphicsSetting-> always included shader后,会将它所有的keywords变体打包到游戏中

+

可以用Shader Variant Collection单独控制keywords,把collection和shader打包到一个AB文件中 +

+
+

参考: +https://zhuanlan.zhihu.com/p/68888831
+https://blog.csdn.net/eevee_1/article/details/118632371
+https://zhuanlan.zhihu.com/p/392004640 +https://blog.csdn.net/danteshenqu/article/details/78170745 +https://zhuanlan.zhihu.com/p/83780152

+

关于 GraphicsSetting-> always included shader说明:
+如果打AB时不想shader被打包进AB包,则用 always included shader 添加shader,单只能是unity buildin 内置shader(Packages内的shader不是内置的)。
+
+
+cube11和cube1使用的shader是Unlit/Color,Cube22和Cube2使用的shader是custom/Cube2;每个ab文件内只有一个cube物体的prefab,引用一个材质球和对应的shader
+AB文件cube11和cube22是在always included shader添加前打包的,AB文件Cube1和Cube2是在always included shader添加后打包的
+根据上面打包测试分析出结果:
+- Cube22和Cube2使用自定义shader custom/Cube2 ,不管always included shader内是否添加该shader,打AB包都会把该shader打包进去
+- Cube11和Cube1使用内置shader Unlit/Color,always included shader内添加该shader,打包AB包不会把该内置shader打包进去;否则会打包进AB包内 +- URP内Shader算是自定义shader,不管always included shader内是否添加,都会将使用的URP Shader打包到对应AB包中

+
+

关于URP Shader打包问题

+
+

由于URP内部的ShaderPackages中,不能使用Inspector面板设置AssetBundleName及脚本代码设置AssetBundleName。在Graphics面板中添加URP shader,但是打包还是会被打进去 +解决方案: + 1. 使用AssetBundleBuild方式打包可以控制Packages内的资源 + 2. 使用Addressables官方插件打包可以设置Packages内的资源

+

材质球和shader打包到一起,会根据材质球引用的keywords变体打包,而加入了GraphicsSetting-> always included shader后,会将它所有的keywords变体打包到游戏中

+

关于shader变体说明:https://blog.csdn.net/kuangben2000/article/details/105400835

+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git "a/ANote/\346\223\215\344\275\234\347\263\273\347\273\237\347\254\224\350\256\260/index.html" "b/ANote/\346\223\215\344\275\234\347\263\273\347\273\237\347\254\224\350\256\260/index.html" new file mode 100644 index 00000000..af3bafcf --- /dev/null +++ "b/ANote/\346\223\215\344\275\234\347\263\273\347\273\237\347\254\224\350\256\260/index.html" @@ -0,0 +1,8926 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 操作系统笔记 - 王国庆的博客 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + 跳转至 + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ +
+ + + + + + + + + +

操作系统笔记

+
+

线程是调度的基本单位,进程则是资源分配的基本单位

+
+

进程间通信

+

管道 pipe

+
+

所谓的管道,就是内核⾥⾯的⼀串缓存
+管道这种通信⽅式效率低,不适合进程间频繁地交换数据

+
+

匿名管道 |

+
ps auxf | grep mysql
+
+

命名管道 FIFO

+
# 创建命名管道
+mkfifo myPipe
+
+mrwang@CodingdeMBP learn % mkfifo myPipe
+# linux 一切皆是文件,管道也是文件 类型 p
+mrwang@CodingdeMBP learn % ls -l
+total 0
+prw-r--r--  1 mrwang  staff  0  2 13 14:55 myPipe
+mrwang@CodingdeMBP learn % 
+
+

消息队列

+
+

消息队列是保存在内核中的消息链表 +在发送数据时,会分成⼀个⼀个独⽴的数据单元,也就是消 +息体(数据块),消息体是⽤户⾃定义的数据类型,消息的发送⽅和接收⽅要约定好消息体的数据类型,所以每个消息体都是固定⼤⼩的存储块,不像管道是⽆格式的字节流数据。如果进程从消息队列中读取了消息体,内核就会把这个消息体删除。

+

消息队列⽣命周期随内核,如果没有释放消息队列或者没有关闭操作系统,消息队列会⼀直存在,⽽前⾯提到的匿名管道的⽣命周期,是随进程的创建⽽建⽴,随进程的结束⽽销毁。

+

消息队列不适合⽐较⼤数据的传输,因为在内核中每个消息体都有⼀个最⼤⻓度的限制,同时所有队列所包含的全部消息体的总⻓度也是有上限。在 Linux 内核中,会有两个宏定义 MSGMAX 和 MSGMNB ,它们以字节为单位,分别定义了⼀条消息的最⼤⻓度和⼀个队列的最⼤⻓度。

+

消息队列通信过程中,存在⽤户态与内核态之间的数据拷⻉开销,因为进程写⼊数据到内核中的消息队列时,会发⽣从⽤户态拷⻉数据到内核态的过程,同理另⼀进程读取内核中的消息数据时,会发⽣从内核态拷⻉数据到⽤户态的过程。

+
+

共享内存

+
+

共享内存的机制,就是拿出⼀块虚拟地址空间来,映射到相同的物理内存中。这样这个进程写⼊的东⻄,另外⼀个进程⻢上就能看到了,都不需要拷⻉来拷⻉去,传来传去,⼤⼤提⾼了进程间通信的速度。

+

⽤了共享内存通信⽅式,带来新的问题,那就是如果多个进程同时修改同⼀个共享内存,很有可能就冲突了。例如两个进程都同时写⼀个地址,那先写的那个进程会发现内容被别⼈覆盖了。

+
+

信号量

+
+

为了防⽌多进程竞争共享资源,⽽造成的数据错乱,所以需要保护机制,使得共享的资源,在任意时刻只能被⼀个进程访问。正好,信号量就实现了这⼀保护机制。

+

信号量其实是⼀个整型的计数器,主要⽤于实现进程间的互斥与同步,⽽不是⽤于缓存进程间通信的数 +据。

+
+

信号

+
+

上⾯说的进程间通信,都是常规状态下的⼯作模式。对于异常情况下的⼯作模式,就需要⽤「信号」的⽅

+

信号是进程间通信机制中唯⼀的异步通信机制,因为可以在任何时候发送信号给某⼀进程,⼀旦有信号产⽣,我们就有下⾯这⼏种,⽤户进程对信号的处理⽅式 +式来通知进程。
+1.执⾏默认操作。Linux 对每种信号都规定了默认操作,例如,上⾯列表中的 SIGTERM 信号,就是终⽌进程的意思。
+2.捕捉信号。我们可以为信号定义⼀个信号处理函数。当信号发⽣时,我们就执⾏相应的信号处理函数。
+3.忽略信号。当我们不希望处理某些信号的时候,就可以忽略该信号,不做任何处理。有两个信号是应⽤进程⽆法捕捉和忽略的,即 SIGKILL 和 SEGSTOP ,它们⽤于在任何时候中断或结束某⼀进程。

+
+

Socket

+
+

前⾯提到的管道、消息队列、共享内存、信号量和信号都是在同⼀台主机上进⾏进程间通信,那要想跨⽹络与不同主机上的进程之间通信,就需要 Socket 通信了
+实际上,Socket 通信不仅可以跨⽹络与不同主机的进程间通信,还可以在同主机上进程间通信。

+
+

根据创建 socket 类型的不同,通信的⽅式也就不同: +- 实现 TCP 字节流通信: socket 类型是 AF_INET 和 SOCK_STREAM; +- 实现 UDP 数据报通信:socket 类型是 AF_INET 和 SOCK_DGRAM; +- 实现 同一台主机上进程间通信: 「本地字节流 socket 」类型是 AF_LOCAL 和 SOCK_STREAM,「本地数据报 socket 」类型是 AF_LOCAL 和 SOCK_DGRAM。另外,AF_UNIXAF_LOCAL 是等价的,所以AF_UNIX 也属于本地 socket;

+

本地字节流 socket 和 本地数据报 socket 在 bind 的时候,不像 TCP 和 UDP 要绑定 IP 地址和端⼝,⽽是绑定⼀个本地⽂件,这也就是它们之间的最⼤区别。

+

总结

+

由于每个进程的⽤户空间都是独⽴的,不能相互访问,这时就需要借助内核空间来实现进程间通信,原因很简单,每个进程都是共享⼀个内核空间。

+

Linux 内核提供了不少进程间通信的⽅式,其中最简单的⽅式就是管道,管道分为「匿名管道」和「命名管道」。

+

匿名管道顾名思义,它没有名字标识,匿名管道是特殊⽂件只存在于内存,没有存在于⽂件系统中,shell命令中的「 | 」竖线就是匿名管道,通信的数据是⽆格式的流并且⼤⼩受限,通信的⽅式是单向的,数据只能在⼀个⽅向上流动,如果要双向通信,需要创建两个管道,再来匿名管道是只能⽤于存在⽗⼦关系的进程间通信,匿名管道的⽣命周期随着进程创建⽽建⽴,随着进程终⽌⽽消失。

+

命名管道突破了匿名管道只能在亲缘关系进程间的通信限制,因为使⽤命名管道的前提,需要在⽂件系统创建⼀个类型为 p 的设备⽂件,那么毫⽆关系的进程就可以通过这个设备⽂件进⾏通信。另外,不管是匿名管道还是命名管道,进程写⼊的数据都是缓存在内核中,另⼀个进程读取数据时候⾃然也是从内核中获取,同时通信数据都遵循先进先出原则,不⽀持 lseek 之类的⽂件定位操作。

+

消息队列克服了管道通信的数据是⽆格式的字节流的问题,消息队列实际上是保存在内核的「消息链表」,消息队列的消息体是可以⽤户⾃定义的数据类型,发送数据时,会被分成⼀个⼀个独⽴的消息体,当然接收数据时,也要与发送⽅发送的消息体的数据类型保持⼀致,这样才能保证读取的数据是正确的。消息队列通信的速度不是最及时的,毕竟每次数据的写⼊和读取都需要经过⽤户态与内核态之间的拷⻉过程。

+

共享内存可以解决消息队列通信中⽤户态与内核态之间数据拷⻉过程带来的开销,它直接分配⼀个共享空间,每个进程都可以直接访问,就像访问进程⾃⼰的空间⼀样快捷⽅便,不需要陷⼊内核态或者系统调⽤,⼤⼤提⾼了通信的速度,享有最快的进程间通信⽅式之名。但是便捷⾼效的共享内存通信,带来新的问题,多进程竞争同个共享资源会造成数据的错乱。

+

那么,就需要信号量来保护共享资源,以确保任何时刻只能有⼀个进程访问共享资源,这种⽅式就是互斥访问。信号量不仅可以实现访问的互斥性,还可以实现进程间的同步,信号量其实是⼀个计数器,表示的是资源个数,其值可以通过两个原⼦操作来控制,分别是 P 操作和 V 操作。

+

与信号量名字很相似的叫信号,它俩名字虽然相似,但功能⼀点⼉都不⼀样。信号是进程间通信机制中唯⼀的异步通信机制,信号可以在应⽤进程和内核之间直接交互,内核也可以利⽤信号来通知⽤户空间的进程发⽣了哪些系统事件,信号事件的来源主要有硬件来源(如键盘 Cltr+C )和软件来源(如 kill 命令),⼀旦有信号发⽣,进程有三种⽅式响应信号 1. 执⾏默认操作、2. 捕捉信号、3. 忽略信号。有两个信号是应⽤进程⽆法捕捉和忽略的,即 SIGKILL 和 SEGSTOP ,这是为了⽅便我们能在任何时候结束或停⽌某个进程。

+

前⾯说到的通信机制,都是⼯作于同⼀台主机,如果要与不同主机的进程间通信,那么就需要 Socket 通信了。Socket 实际上不仅⽤于不同的主机进程间通信,还可以⽤于本地主机进程间通信,可根据创建Socket 的类型不同,分为三种常⻅的通信⽅式,⼀个是基于 TCP 协议的通信⽅式,⼀个是基于 UDP 协议的通信⽅式,⼀个是本地进程间通信⽅式。

+

多线程同步

+
+

线程之间是可以共享进程的资源,⽐如代码段、堆空间、数据段、打开的⽂件等资源,但每个线程都有⾃⼰独⽴的栈空间。
+那么问题就来了,多个线程如果竞争共享资源,如果不采取有效的措施,则会造成共享数据的混乱。

+
+

当获取不到锁时,线程就会⼀直 wile 循环,不做任何事情,所以就被称为「忙等待锁」,也被称为 ⾃旋锁(spin lock)。 +那当没获取到锁的时候,就把当前线程放⼊到锁的等待队列,然后执⾏调度程序,把 CPU让给其他线程执⾏,称为 「⽆等待锁」。

+

+
+

最底层的两种就是会「互斥锁和⾃旋锁」,有很多⾼级的锁都是基于它们实现的,你可以认为它们是各种锁的地基,所以我们必须清楚它俩之间的区别和应⽤。 +加锁的⽬的就是保证共享资源在任意时间⾥,只有⼀个线程访问,这样就可以避免多线程导致共享数据错乱的问题。

+
+

当已经有⼀个线程加锁后,其他线程加锁则就会失败,互斥锁和⾃旋锁对于加锁失败后的处理⽅式是不⼀样的:

+
    +
  • 互斥锁加锁失败后,线程会释放 CPU ,给其他线程;
  • +
  • ⾃旋锁加锁失败后,线程会忙等待,直到它拿到锁;
  • +
+

互斥锁是⼀种「独占锁」,⽐如当线程 A 加锁成功后,此时互斥锁已经被线程 A 独占了,只要线程 A 没有释放⼿中的锁,线程 B 加锁就会失败,于是就会释放 CPU 让给其他线程,既然线程 B 释放掉了 CPU,⾃然线程 B 加锁的代码就会被阻塞

+

对于互斥锁加锁失败⽽阻塞的现象,是由操作系统内核实现的。当加锁失败时,内核会将线程置为「睡眠」状态,等到锁被释放后,内核会在合适的时机唤醒线程,当这个线程成功获取到锁后,于是就可以继续执⾏。如下图: + +所以,互斥锁加锁失败时,会从⽤户态陷⼊到内核态,让内核帮我们切换线程,虽然简化了使⽤锁的难度,但是存在⼀定的性能开销成本。

+

那这个开销成本是什么呢?会有两次线程上下⽂切换的成本
+- 当线程加锁失败时,内核会把线程的状态从「运⾏」状态设置为「睡眠」状态,然后把 CPU 切换给其他线程运⾏; +- 接着,当锁被释放时,之前「睡眠」状态的线程会变为「就绪」状态,然后内核会在合适的时间,把CPU 切换给该线程运⾏。

+

线程的上下⽂切换的是什么?当两个线程是属于同⼀个进程,因为虚拟内存是共享的,所以在切换时,虚 +拟内存这些资源就保持不动,只需要切换线程的私有数据、寄存器等不共享的数据。 +上下切换的耗时有⼤佬统计过,⼤概在⼏⼗纳秒到⼏微秒之间,如果你锁住的代码执⾏时间⽐较短,那可 +能上下⽂切换的时间都⽐你锁住的代码执⾏时间还要⻓。 +所以,如果你能确定被锁住的代码执⾏时间很短,就不应该⽤互斥锁,⽽应该选⽤⾃旋锁,否则使⽤互斥 +锁。 +⾃旋锁是通过 CPU 提供的 CAS 函数(Compare And Swap),在「⽤户态」完成加锁和解锁操作,不 +会主动产⽣线程上下⽂切换,所以相⽐互斥锁来说,会快⼀些,开销也⼩⼀些。 +⼀般加锁的过程,包含两个步骤: +第⼀步,查看锁的状态,如果锁是空闲的,则执⾏第⼆步; +第⼆步,将锁设置为当前线程持有; +CAS 函数就把这两个步骤合并成⼀条硬件级指令,形成原⼦指令,这样就保证了这两个步骤是不可分割 +的,要么⼀次性执⾏完两个步骤,要么两个步骤都不执⾏。 +使⽤⾃旋锁的时候,当发⽣多线程竞争锁的情况,加锁失败的线程会「忙等待」,直到它拿到锁。这⾥的 +「忙等待」可以⽤ while 循环等待实现,不过最好是使⽤ CPU 提供的 PAUSE 指令来实现「忙等 +待」,因为可以减少循环等待时的耗电量。 +⾃旋锁是最⽐较简单的⼀种锁,⼀直⾃旋,利⽤ CPU 周期,直到锁可⽤。需要注意,在单核 CPU 上,需 +要抢占式的调度器(即不断通过时钟中断⼀个线程,运⾏其他线程)。否则,⾃旋锁在单 CPU 上⽆法使 +⽤,因为⼀个⾃旋的线程永远不会放弃 CPU。 +⾃旋锁开销少,在多核系统下⼀般不会主动产⽣线程切换,适合异步、协程等在⽤户态切换请求的编程⽅ +式,但如果被锁住的代码执⾏时间过⻓,⾃旋的线程会⻓时间占⽤ CPU 资源,所以⾃旋的时间和被锁住的 +代码执⾏的时间是成「正⽐」的关系,我们需要清楚的知道这⼀点。 +⾃旋锁与互斥锁使⽤层⾯⽐较相似,但实现层⾯上完全不同:当加锁失败时,互斥锁⽤「线程切换」来应 +对,⾃旋锁则⽤「忙等待」来应对。 +它俩是锁的最基本处理⽅式,更⾼级的锁都会选择其中⼀个来实现,⽐如读写锁既可以选择互斥锁实现, +也可以基于⾃旋锁实现。

+

互斥锁

+

⾃旋锁

+

读写锁

+

乐观锁

+

悲观锁

+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git "a/ANote/\346\227\245\345\270\270\344\275\277\347\224\250\345\221\275\344\273\244/index.html" "b/ANote/\346\227\245\345\270\270\344\275\277\347\224\250\345\221\275\344\273\244/index.html" new file mode 100644 index 00000000..19b4f371 --- /dev/null +++ "b/ANote/\346\227\245\345\270\270\344\275\277\347\224\250\345\221\275\344\273\244/index.html" @@ -0,0 +1,8476 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 日常使用命令 - 王国庆的博客 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + 跳转至 + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+ +
+ + + + + + + + + +

日常使用命令

+

打开文件或者目录:

+
    +
  1. +

    Mac 打开文件或者目录使用 open ./

    +
  2. +
  3. +

    Windows 打开文件或者目录使用 start ./

    +
  4. +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git "a/ANote/\346\227\245\345\270\270\347\254\224\350\256\260/index.html" "b/ANote/\346\227\245\345\270\270\347\254\224\350\256\260/index.html" new file mode 100644 index 00000000..323ae93a --- /dev/null +++ "b/ANote/\346\227\245\345\270\270\347\254\224\350\256\260/index.html" @@ -0,0 +1,8562 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 日常笔记 - 王国庆的博客 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + 跳转至 + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+ +
+
+ + + +
+ +
+ + + + + + + + + +

日常笔记

+

JetBrains之全家桶破解(idea,rider....)

+

https://ckey.run/ + 激活: + Windows:复制命令 irm ckey.run|iex使用Win + X按键,选择WindowsPowerShell(管理员)运行即可全自动激活! + Linux:复制命令 wget -q ckey.run -O ckey.run && bash ckey.run使用终端执行即可全自动激活! + Mac:复制命令 curl -Ls ckey.run -o ckey.run && bash ckey.run使用终端执行即可全自动激活!

+
+

已经失效 +1. 打开链接 https://3.jetbra.in/ +2. 随便点击下图中一个可用的链接,进入之后看下一步。 + +3. 先先下载 jetbra.zip

+

+

下载后解压,解压文件夹放到一个文件夹下(目录没有空格和中文)

+

+

关闭rider,然后运行jetbra\scripts\install-all-users.vbs脚本,等待执行完,出现Done +4. 点击Rider图标,复制key

+

+

然后粘贴激活 + +

+
+

Java环境安装

+
    +
  1. +

    jdk的下载可以直接在官网进行,链接为http://www.oracle.com/technetwork/java/javase/downloads/index.html +至于版本下载8还是9请自行决定,推荐8版本 +

    +
  2. +
  3. +

    添加环境变量

    +
  4. +
+

+ +

+
JAVA_HOME
+C:\Program Files\Java\jdk1.8.0_121
+
+CLASSPATH
+%JAVA_HOME%\lib;%JAVA_HOME%\lib\tool.jar
+
+在变量Path中追加
+%JAVA_HOME%\bin;%JAVA_HOME%\jre\bin
+
+

NAS 相关

+
+

https://doc.sun-panel.top/zh_cn/
+https://registry.hub.docker.com/r/hslr/sun-panel/
+https://www.zhihu.com/question/603336478/answer/3406593764

+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git "a/ANote/\346\242\263\347\220\206/index.html" "b/ANote/\346\242\263\347\220\206/index.html" new file mode 100644 index 00000000..845bd2ed --- /dev/null +++ "b/ANote/\346\242\263\347\220\206/index.html" @@ -0,0 +1,8407 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 梳理 - 王国庆的博客 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + 跳转至 + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+ +
+ + + + + + + + + +

梳理

+

网络 json zip压缩,10000字节压缩后大约是2000字节,100字节压缩后大约100或者大约100

+

热更新 apple store 审查到发布 到玩家看到版本包有一 +个过程 (审查版本,发布后打开后热更(审查屏蔽的东西),老版本热更(不更整个包)支持) + LZMA 4倍,,LZ4 3倍,

+

加密 streamassetpath加载到内存中在android上只有WWW,不能用别的,ab文件的字节流没有办法加载到内存中然后解密,所以直接加密lua文件,在加载lua时解密

+

设计模式

+

文字加载(斜体)

+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git "a/ANote/\347\237\251\351\230\265\347\254\224\350\256\260/index.html" "b/ANote/\347\237\251\351\230\265\347\254\224\350\256\260/index.html" new file mode 100644 index 00000000..921b62c5 --- /dev/null +++ "b/ANote/\347\237\251\351\230\265\347\254\224\350\256\260/index.html" @@ -0,0 +1,8544 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 矩阵笔记 - 王国庆的博客 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + 跳转至 + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+ +
+ + + + + + + + + +

矩阵笔记

+

Matrix4x4

+
    +
  • 在unity shader中,矩阵元素的填充是按行优先填充的
  • +
  • 在Matrix4x4中,是按列优先填充的
    +

    Matrix4x4中,matrix.m12 这个表示第一行二列,
    +20210625172027

    +
    +
  • +
+
//一个带有由所传递的平面坐标定义的视锥截面的投影矩阵
+public static Matrix4x4 Frustum(float left, float right, float bottom, float top, float zNear, float zFar);
+//构造正交矩阵(这个在之前的贴花着色器脚本代码中用到过)
+public static Matrix4x4 Ortho(float left, float right, float bottom, float top, float zNear, float zFar);
+//构造透视投影矩阵
+public static Matrix4x4 Perspective(float fov, float aspect, float zNear, float zFar);
+//构造旋转矩阵
+public static Matrix4x4 LookAt(Vector3 from, Vector3 to, Vector3 up);
+public static Matrix4x4 Rotate(Quaternion q);
+//构造缩放矩阵
+public static Matrix4x4 Scale(Vector3 vector);
+//从位置,旋转,缩放构建矩阵
+public static Matrix4x4 TRS(Vector3 pos, Quaternion q, Vector3 s);
+
+
//转置
+public Matrix4x4 transpose { get; }
+//是否为单位矩阵
+public bool isIdentity { get; }
+//行列式
+public float determinant { get; }
+//逆
+public Matrix4x4 inverse { get; }
+
+

LookAt 实现

+
    /// <summary>
+    /// LookAt
+    /// 同时也是基变换的一种矩阵
+    /// </summary>
+    /// <param name="from">eye</param>
+    /// <param name="to">target</param>
+    /// <param name="up"></param>
+    /// <returns></returns>
+    public static Matrix4x4 LookAt(Vector3 from,Vector3 to,Vector3 up)
+    {
+        // link: https://learnopengl-cn.github.io/01%20Getting%20started/09%20Camera/
+        Vector3 z=Vector3.Normalize(to-from); // cam dir  , cam forward 前(z轴)
+        Vector3 x=Vector3.Normalize(GeometryTools.Cross(up,z)); // cam right dir 右(x轴)
+        Vector3 y=GeometryTools.Cross(z,x); // cam up dir 上(y轴)
+        Matrix4x4 mat=new Matrix4x4();
+        mat.SetColumn(0,x);
+        mat.SetColumn(1,y);
+        mat.SetColumn(2,z);
+        mat.SetColumn(3,new Vector4(from.x,from.y,from.z,1));
+        return mat;
+    }
+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git "a/ANote/\350\247\206\351\242\221\345\244\204\347\220\206/index.html" "b/ANote/\350\247\206\351\242\221\345\244\204\347\220\206/index.html" new file mode 100644 index 00000000..6d9aef43 --- /dev/null +++ "b/ANote/\350\247\206\351\242\221\345\244\204\347\220\206/index.html" @@ -0,0 +1,8615 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 视频处理 - 王国庆的博客 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + 跳转至 + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ +
+ + + + + + + + + +

视频处理

+

合并视频(拼接视频)

+
ffmpeg -i "concat:1.mp4|2.mp4|3.mp4" -codec copy out_mp4.mp4
+
+
ffmpeg -f concat -safe 0 -i join.txt -c copy output.mp4
+
+

join.txt文件

+
file /home/sk/myvideos/part1.mp4
+file /home/sk/myvideos/part2.mp4
+file '/home/sk/myvideos/part3.mp4'
+file '/home/sk/myvideos/part4.mp4'
+
+

分割视频

+
# 切分视频文件为多个部分
+ffmpeg -i part.mp4 -t 00:40:22 -c copy part1-1.mp4 -ss 00:40:22 -t 01:37:15 -codec copy part1-2.mp4  -ss 02:17:37 -codec copy part1-3.mp4
+
+
+
+
+ffmpeg -i audio.mp3 -ss 00:01:54 -to 00:06:53 -c copy output.mp3
+
+ffmpeg -i input.mp4 -ss 00:00:50 -codec copy -t 50 output.mp4
+
+# –s – 表示视频剪辑的开始时间。在我们的示例中,开始时间是第 50 秒。
+# -t – 表示总的持续时间。
+
+

视频添加字幕

+

给mkv文件添加srt格式的软字幕:

+
ffmpeg -i input.mkv -i input.srt -c copy -c:s srt output.mkv
+
+

给mp4文件添加srt格式的软字幕(mkv格式本身支持字幕轨道,但mp4需要使用mov_text)

+
ffmpeg -i input.mp4 -i input.srt -c copy -c:s mov_text output.mp4
+
+

给mkv文件添加ass软字幕(mp4不支持ass编码格式的字幕流,mkv支持)

+
ffmpeg -i input.mkv -i input.ass -c copy -c:s ass output.mkv
+
+

增加字幕流

+
ffmpeg -i video.avi -i sub.ass -map 0:0 -map 0:1 -map 1 -c:a copy -c:v copy -c:s copy video.mkv
+
+

提取字幕流 +1)原始文本输出

+
ffmpeg -i output.mkv -an -vn -bsf:s mov2textsub -scodec copy -f rawvideo sub.txt
+ffmpeg -i output.mkv -an -vn -c:s copy -f rawvideo -map 0:s sub2.txt
+
+

2)ass格式输出

+
ffmpeg -i output.mkv -an -vn -scodec copy sub3.ass
+
+

视频提取截图

+
ffmpeg -ss 00:43:55 -i video.mp4  -f image2  -vframes 1 -y frame.png
+# 注意将ss放到最前面可以加快速度, -y代表覆盖文件 -vframes代表帧数 -i代表输入,即in;-ss也可以使用单个数字,代表秒数,从0开始计算。
+
+

视频去固定水印

+
ffmpeg -i video.mp4 -vf "delogo=x=1680:y=60:w=160:h=55"  -y  new_1.mp4
+
+
+

这里-vf表示video filter, 其中delogo的参数代表水印的坐标和大小,把视频左上角作为坐标原点,横向为x轴,纵向为y轴。这种情况除非预先知道水印的位置和大小,否则不是特别方便,当然,准确识别水印位置也是一个难点,不是很轻易能实现的。 +可能根据某些ffmpeg版本不同,需要加-strict experimental 参数,一种情况是比较老的版本音频ACC属于实验阶段,可以按情况设置或者升级ffmpeg版本。

+
+

测试
+ffmpeg -i "你需要转换格式的文件(附带绝对路径).mkv" -vcodec copy -acodec aac "你想要转换成的格式(附带绝对路径).mp4"

+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git "a/ANote/\350\256\260\345\275\225/index.html" "b/ANote/\350\256\260\345\275\225/index.html" new file mode 100644 index 00000000..c1c87496 --- /dev/null +++ "b/ANote/\350\256\260\345\275\225/index.html" @@ -0,0 +1,8561 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 记录 - 王国庆的博客 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + 跳转至 + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+ +
+ + + + + + + + + +

记录

+

面试 socket整理一下资料 +fin ,rst包分别在什么时候发 +Linger=true为啥close(0)发rst包

+

断线重连

+

zlib,loom,sevenlib,ngui,a*,fingergesture,t4m,besthttp,itween,dotween,uniwebview,easytouch,meshbaker,spine,minijson

+

贴图压缩(在屏幕显示范围大的不压缩,显示小的压缩)

+

assetbundle压缩(BuildAssetBundleOptions.ChunkBasedCompression)

+

assetbundle压缩比例 lzma 4倍,lz4 3倍

+

navigation寻路(性能测试)

+

a*寻路

+

5.3.2动画问题 动态加载烘培问题

+

assetbundle +--资源卸载(Resources.UnloadUnusedAssets() 异步) +--AssetBundle.LoadFromMemory() 加载比较慢

+

ui适配 等比缩放 +NGUi: +Flexible:这样方式保存的场景和控件大小是固定的,如果你设置的像素值为300*200那么在屏幕比较小的设备上,它会显的比较大;屏幕比较大的设备上会显得比较小;其实就是大小不会根据屏幕大小变化而变化。这个不符合我们屏幕自适应的要求,应该也不会常用。 +Constrained:这个与上面那种方式是完全相反的,他不会固定一个值,而是随着设备屏幕大小的变化而变化,大小是依据百分比,但是要注意,在根据百分比缩放的同时 画面有可能会失真。 +ConstrainedOnMobiles:这个我没太理解,大概意思应该是在桌面设备上它会以 Flexible 的方式进行显示,如果是移动设备的话它将以 Constrained 的方式显示。

+

aspect(宽高比) +standardAspect=1280/720=1.78 +aspect1=1.3 +aspect2=1.8 +aspect1<standardAspect<aspect2 +---适配高度 +aspect1:高度太大,适配高度后,宽方向ui出去了 。所以适配宽度方向(unity可以调整Camera的size=standardAspect/aspect1,在standardAspect时 size=1。这样等于适配宽度方向,高度有黑边然后把高度方向的背景图做大1280*960,aspect=1.3,一般没有小于1.3得了) +aspect2:宽度太大,适配高度后,宽方向ui有黑边 (但是一般没有大于1.7的比例了) +---适配宽度 +aspect1:高度太大,适配宽度后,高方向ui有黑边 +aspect2:宽度太大,适配宽度后,高方向ui出去了

+

etc etc2

+

fbx 的read/write 可以关闭 但是粒子特效的mesh需要打开,粒子系统通常需要动态地修改其粒子的顶点属性 ,将FBX上的Read/Write Enabled关闭后,内存中便不再保存其Mesh的副本(只存在显存中),因此其属性就不可再被访问和修改。

+

=> 拉姆达表达式

+

断点续传

+

正则表达式 平衡组/递归匹配 固化分组 (几层括号嵌套);@"{([^{}]+|{(?)|}(?<-Open>))*(?(Open)(?!))}|[^{},]+"

+

ftp C#工程ftp上传没问题,unity工程里面上传失败(ftp 的socket 没有优雅的关闭,先shutdown 再 close 注意有个选项Linger 有数据是否在关闭时逗留 )

+

打包删除场景,进入场景怎么进去的??

+

MemoryStream 在调用Close()和Dispose()后 仍然可以使用ToArray()获取数据的字节数组

+

Finalize Dispose Close 用于非托管资源 ,其它时候没有意义 +http://www.cnblogs.com/liuning8023/archive/2012/07/22/2603819.html +---Finalize方法(C#中是析构函数,以下称析构函数)是用于释放非托管资源的,而托管资源会由GC自动回收。 +---Finalize 在C#中就是指析构函数,该方法默认存在,只有在定义析构函数后在析构函数中会隐式的调用Finalize,只有定义析构函数后终结器才能看到Finalize或者析构函数 +您无法从 C# 或 C++ 编程语言的托管扩展中调用或重写 Object.Finalize 方法。C# 和托管扩展提供析构函数作为编写终止代码的机制。在 C# 和托管扩展中,您必须使用析构函数语法来执行清理操作。因为该语法隐式地为对象基类调用 Finalize 方法,所以十分方便易用。这保证了对当前类从其导出的所有级别的析构函数都调用了 Finalize。 +---Dispose 只是用于显式的释放对象,在Dispose中默认会先调用Close,dispose是主动释放,一般用using语法可以代为实现 +---using语句块执行完默认会执行Dispose +---GC.SuppressFinalize(this); 这个方法是被客户直接调用的,告诉垃圾回收器从Finalization队列中清除自己,从而阻止垃圾回收器调用 析构函数 方法 +---在.net framework 里面,close()被设计成public的,并且在close()里面调用被隐藏的dispose(),而后dispose()再去调用另一个virtual的dispose(bool)

+

终结器(Finalize方法): +垃圾回收器使用名为“终止队列”的内部结构跟踪具有 Finalize 方法的对象。每次您的应用程序创建具有 Finalize 方法的对象时,垃圾回收器都在终止队列中放置一个指向该对象的项。托管堆中所有需要在垃圾回收器回收其内存之前调用它们的终止代码的对象都在终止队列中含有项。(实现Finalize方法或析构函数对性能可能会有负面影响,因此应避免不必要地使用它们。用Finalize方法回收对象使用的内存需要至少两次垃圾回收。当垃圾回收器执行回收时,它只回收没有终结器的不可访问对象的内存。这时,它不能回收具有终结器的不可访问对象。它改为将这些对象的项从终止队列中移除并将它们放置在标为准备终止的对象列表中。该列表中的项指向托管堆中准备被调用其终止代码的对象。垃圾回收器为此列表中的对象调用Finalize方法,然后将这些项从列表中移除。后来的垃圾回收将确定终止的对象确实是垃圾,因为标为准备终止对象的列表中的项不再指向它们。在后来的垃圾回收中,实际上回收了对象的内存。概括而言,就是将垃圾回收分为了三个阶段,第一个阶段回收没有Finalize方法或者析构函数的对象,第二个阶段调用那些析构函数或者Finalize方法,第三个阶段回收那些刚调用了析构函数或者Finalize方法的对象。)

+

终结器(finalizer)是在回收过程中,由垃圾回收器调用的方法。如何含有终结器的对象到了G2中,那么就会需要非常长的时间来回收。事实上,根据应用程序运行时间的长短,对象很有机会直到应用程序退出之前都不会被回收(特别是其中包含的重要的资源得不得释放,将会对性能产生很大的影响,比如说数据库连接得不到释放。)

+

实现模型: +1、由于大多数的非托管资源都要求可以手动释放,所以,我们应该专门为释放非托管资源公开一个方法。实现IDispose接口的Dispose方法是最好的模型,因为C#支持using语句快,可以在离开语句块时自动调用Dispose方法。 +2、虽然可以手动释放非托管资源,我们仍然要在析构函数中释放非托管资源,这样才是安全的应用程序。否则如果因为程序员的疏忽忘记了手动释放非托管资源, 那么就会带来灾难性的后果。所以说在析构函数中释放非托管资源,是一种补救的措施,至少对于大多数类来说是如此。 +3、由于析构函数的调用将导致GC对对象回收的效率降低,所以如果已经完成了析构函数该干的事情(例如释放非托管资源),就应当使用SuppressFinalize方法告诉GC不需要再执行某个对象的析构函数。 +4、析构函数中只能释放非托管资源而不能对任何托管的对象/资源进行操作。因为你无法预测析构函数的运行时机,所以,当析构函数被执行的时候,也许你进行操作的托管资源已经被释放了。这样将导致严重的后果。 +5、(这是一个规则)如果一个类拥有一个实现了IDispose接口类型的成员,并创建(注意是创建,而不是接收,必须是由类自己创建)它的实例对象,则 这个类也应该实现IDispose接口,并在Dispose方法中调用所有实现了IDispose接口的成员的Dispose方法。 +只有这样的才能保证所有实现了IDispose接口的类的对象的Dispose方法能够被调用到,确保可以手动释放任何需要释放的资源。

+

Finalize()特性:

+

重写Finalize()的唯一原因是,c#类通过PInvoke或复杂的COM互操作性任务使用了非托管资源(典型的情况是通过System.Runtime.InteropServices.Marshal类型定义的各成员)注:PInvoke是平台调用服务。 +object中有finalize方法,但创建的类不能重写此方法,若Overide会报错,只能通过析构函数来达到同样的效果。 +Finalize方法的作用是保证.NET对象能在垃圾回收时清除非托管资源。 +在CLR在托管堆上分配对象时,运行库自动确定该对象是否提供一个自定义的Finalize方法。如果是这样,对象会被标记为可终结的,同时一个指向这个对象的指针被保存在名为终结队列的内部队列中。终结队列是一个由垃圾回收器维护的表,它指向每一个在从堆上删除之前必须被终结的对象。 +注意:Finalize虽然看似手动清除非托管资源,其实还是由垃圾回收器维护,它的最大作用是确保非托管资源一定被释放。 +在结构上重写Finalize是不合法的,因为结构是值类型,不在堆上,Finalize是垃圾回收器调用来清理托管堆的,而结构不在堆上。 +Dispose()特性:

+

为了更快更具操作性进行释放,而非让垃圾回收器(即不可预知)来进行,可以使用Dispose,即实现IDispose接口. +结构和类类型都可以实现IDispose(与重写Finalize不同,Finalize只适用于类类型),因为不是垃圾回收器来调用Dispose方法,而是对象本身释放非托管资源,如Car.Dispose().如果编码时没有调用Dispose方法,以为着非托管资源永远得不到释放。 +如果对象支持IDisposable,总是要对任何直接创建的对象调用Dispose(),即有实现IDisposable接口的类对象都必须调用Dispose方法。应该认为,如果类设计者选择支持Dispose方法,这个类型就需要执行清除工作。记住一点,如果类型实现了IDisposable接口,调用Dispose方法总是正确的。 +.net基类库中许多类型都实现IDisposable接口,并使用了Dispose的别名,其中一个别名如IO中的Close方法,等等别名。使得看起来更自然。 +using关键字,实际内部也是实现IDisposable方法,用ildasm.exe查看使用了using的代码的CIL,会发现是用try/finally去包含using中的代码,并且在finally中调用dispose方法。 +个人总结:

+

相同点:

+

都是为了确保非托管资源得到释放。 +不同点:

+

finalize由垃圾回收器调用;dispose由对象调用。 +finalize无需担心因为没有调用finalize而使非托管资源得不到释放,而dispose必须手动调用。 +finalize虽然无需担心因为没有调用finalize而使非托管资源得不到释放,但因为由垃圾回收器管理,不能保证立即释放非托管资源;而dispose一调用便释放非托管资源。 +只有类类型才能重写finalize,而结构不能;类和结构都能实现IDispose.原因请看Finalize()特性。

+

class Car +{ + ///

+ /// 析构函数 + /// + ~Car() + { + // cleanup statements...
+ } +} +该析构函数隐式调用对象基类的 Finalize 方法。因此,该析构函数被隐式地转换为如下代码:

+

protected override void Finalize()
+{
+ try + { + // Cleanup statements...
+ }
+ finally + { + base.Finalize();
+ } +} +这意味着,对继承链中的所有实例递归调用 Finalize 方法。 +说明:不要使用空的析构函数。如果类包含析构函数,则 Finalize 队列中则会创建一个项。当调用析构函数时,将调用垃圾回收器(GC)来处理该队列。如果析构函数为空,只会导致不必要的性能损失。 +Object.Finalize 方法

+

允许 Object 在“垃圾回收”回收 Object 之前,尝试释放资源并执行其他清理操作。Finalize 是受保护的,因此只能通过此类或派生类访问它。

+

对象变为不可访问后,将自动调用此方法,除非已通过 GC.SuppressFinalize 调用使对象免除了终结。在应用程序域的关闭过程中,对没有免除终结的对象将自动调用 Finalize,即使那些对象仍是可访问的。对于给定的实例仅自动调用 Finalize 一次,除非使用 GC.ReRegisterForFinalize重新注册该对象,并且后面没有调用 GC.SuppressFinalize。

+

派生类型中的每个 Finalize 实现都必须调用其基类型的 Finalize 实现。这是唯一一种允许应用程序代码调用 Finalize 的情况。

+

注意:C# 编译器不允许你直接实现 Finalize 方法,因此 C# 析构函数自动调用其基类的析构函数。

+

Finalize 操作具有下列限制:

+

1) 垃圾回收过程中执行终结器的准确时间是不确定的。不保证资源在任何特定的时间都能释放,除非调用 Close 方法或 Dispose 方法。

+

2) 即使一个对象引用另一个对象,也不能保证两个对象的终结器以任何特定的顺序运行。即,如果对象 A 具有对对象 B 的引用,并且两者都有终结器,则当对象 A 的终结器启动时,对象 B 可能已经终结了。

+

3) 运行终结器的线程是未指定的。

+

在下面的异常情况下,Finalize 方法可能不会运行完成或可能根本不运行:

+

1) 另一个终结器无限期地阻止(进入无限循环,试图获取永远无法获取的锁,诸如此类)。由于运行时试图运行终结器来完成,所以如果一个终结器无限期地阻止,则可能不会调用其他终结器。

+

2) 进程终止,但不给运行时提供清理的机会。在这种情况下,运行时的第一个进程终止通知是 DLL_PROCESS_DETACH 通知。

+

在关闭过程中,只有当可终结对象的数目继续减少时,运行时才继续 Finalize 对象。

+

如果 Finalize 或 Finalize 的重写引发异常,并且运行库并非寄宿在重写默认策略的应用程序中,则运行库将终止进程,并且不执行任何活动的 try-finally 块或终结器。如果终结器无法释放或销毁资源,此行为可以确保进程完整性。

+

说明:默认情况下,Object.Finalize 不执行任何操作。只有在必要时才必须由派生类重写它,因为如果必须运行 Finalize 操作,垃圾回收过程中的回收往往需要长得多的时间。如果 Object 保存了对任何资源的引用,则 Finalize 必须由派生类重写,以便在垃圾回收过程中,在放弃 Object 之前释放这些资源。当类型使用文件句柄或数据库连接这类在回收使用托管对象时必须释放的非托管资源时,该类型必须实现 Finalize。Finalize 可以采取任何操作,包括在垃圾回收过程中清理了对象后使对象复活(即,使对象再次可访问)。但是,对象只能复活一次;在垃圾回收过程中,不能对复活对象调用 Finalize。

+

析构函数是执行清理操作的 C# 机制。析构函数提供了适当的保护措施,如自动调用基类型的析构函数。在 C# 代码中,不能调用或重写 Object.Finalize。

+

析构函数和Finalize不能同时存在一个类的定义中 +假设有三个类 A->B->C;C继承B,B继承A A有析构函数,C有析构函数 B有Finalize方法 创建C一个实例查看析构情况 +protected void Finalize(){...} C B执行 +protected virtual void Finalize(){...} A执行 +public void Finalize(){...} A C执行 +public virtual void Finalize(){...} 运行直接崩溃(都没有执行) +private void Finalize(){...} A C执行 +private virtual void Finalize() 编译错误(虚拟成员或抽象成员不能是私有的)

+

如果把 Finalize写在C中 AB的析构执行 C的Finalize不执行 +base.Finalize() 调用必须父类中有Finalize定义(父类用析构不行,编译报错,Finalize方法不存在)

+

MemoryStream Position重置为0 但是 Length 不变 Length和Position没有必然关系

+

封箱和拆箱 +---装箱在值类型向引用类型转换时发生 +---拆箱在引用类型向值类型转换时发生

+

List,IList,Array,ArrayList

+

蒙皮网格原理

+

哈希算法

+

动画融合

+

a*算法

+

设计模式

+

ui上的粒子特效

+

Navigation寻路 +-------加速度控制不方便 不能为0,可以设置Vector3的速度变量 +-------不能取寻路数据的高度值 +-------OffMeshLink手动控制路径,一次只能通过一个agent,如果有agent正在通过OffMeshLink路径,其他agent只能在入口等着 不能通过

+

C# WebRequest 超时bug 21秒左右

+

C# http ServicePointManager.Expect100Continue = false; +-----100-continue用于客户端在发送POST数据给服务器前,征询服务器情况,看服务器是否处理POST的数据,如果不处理,客户端则不上传POST数据,如果处理,则POST上传数据。在现实应用中,通过在POST大数据时,才会使用100-continue协议。

+

socket shutdown和close的区别,除了Fork进程引用计数之外还有什么 FTP上传文件后必须用ShutDown(如果设置了Linger为ture)

+

UTF-8, UTF-16, UTF-16LE, UTF-16BE的区别(UTF-16LE 在C#里面是Encoding.unicode) +首先, 我们说的unicode, 其实就是utf-16, 但最通用的却是utf-8, +原因: 我猜大概是英文占的比例比较大, 这样utf-8的存储优势比较明显, 因为utf-16是固定16位的(双字节), 而utf-8则是看情况而定, 即可变长度, 常规的128个ASCII只需要8位(单字节), 而汉字需要24位 +UTF-16, UTF-16LE, UTF-16BE, 及其区别BOM +同样都是unicode, 为什么要搞3种这么麻烦? +先说UTF-16BE (big endian), 比较好理解的, 俗称大头,比如说char 'a', ascii为0x61; 那么它的utf-8, 则为 [0x61], 但utf-16是16位的, 所以为[0x00, 0x61];再说UTF-16LE(little endian), 俗称小头, 这个是比较常用的 .还是char'a', 它的代码却反过来: [0x61, 0x00], 据说是为了提高速度而迎合CPU的胃口, CPU就是这到倒着吃数据的, 这里面有汇编的知识, 不多说 +然后说UTF-16, 要从代码里自动判断一个文件到底是UTF-16LE还是BE, 对于单纯的英文字符来说还比较好办, 但要有特殊字符, 图形符号, 汉字, 法文, 俄语, 火星语之类的话, 相信各位都很头痛吧, 所以, unicode组织引入了BOM的概念, 即byte order mark, 顾名思义, 就是表名这个文件到底是LE还是BE的, +其方法就是, 在UTF-16文件的头2个字节里做个标记: LE [0xFF, 0xFE], BE [0xFE, 0xFF]

+

理解了这个后, 在java里遇到utf-16还是会遇到麻烦, 因为要在文件里面单独判断头2个再字节是很不流畅的 +InputStreamReader reader=new InputStreamReader(fin, charset) (Java代码) +1. 如果这个UTF-16文件里带有BOM的话, charset就用"UTF-16", java会自动根据BOM判断LE还是BE, 如果你在这里指定了"UTF-16LE"或"UTF-16BE"的话, 猜错了会生成乱七八糟的文件, 哪怕猜对了, java也会把头2个字节当成文本输出给你而不会略过去, 因为[FF FE]或[FE FF]这2个代码没有内容, 所以, windows会用"?"代替给你 +2. 如果这个UTF-16文件里不带BOM的话, 则charset就要用"UTF-16LE"或"UTF-16BE"来指定LE还是BE的编码方式 +另外, UTF-8也有BOM的, [0xEF, 0xBB, 0xBF], 但可有可无, 但用windows的notepad另存为时会自动帮你加上这个, 而很多非windows平台的UTF8文件又没有这个BOM, 真是难为我们这些程序员啊 +http://www.iteye.com/topic/583064

+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git "a/ANote/\350\256\276\350\256\241\346\250\241\345\274\217\347\254\224\350\256\260/index.html" "b/ANote/\350\256\276\350\256\241\346\250\241\345\274\217\347\254\224\350\256\260/index.html" new file mode 100644 index 00000000..1360370a --- /dev/null +++ "b/ANote/\350\256\276\350\256\241\346\250\241\345\274\217\347\254\224\350\256\260/index.html" @@ -0,0 +1,10301 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 设计模式笔记 - 王国庆的博客 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + 跳转至 + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+ +
+ + + + + + + + + +

设计模式笔记

+
+

设计模式分为三种类型,共23类。

+

(1)创建型模式:单例模式、抽象工厂模式、建造者模式、工厂模式、原型模式。

+

(2)结构型模式:适配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模式。

+

(3)行为型模式:模版方法模式、命令模式、迭代器模式、观察者模式、中介者模式、备忘录模式、解释器模式、状态模式、策略模式、职责链模式、访问者模式

+
+

六大原则

+
    +
  • 开闭原则:对扩展开放,对修改关闭。
  • +
  • 里氏替换原则:继承必须确保超类所拥有的性质在子类中仍然成立。
  • +
  • 依赖倒置原则:要面向接口编程,不要面向实现编程。
  • +
  • 接口隔离原则:要为各个类建立他们需要专用接口。
  • +
  • 迪米特法则:只与你的朋友直接交谈,不跟陌生人说话。
  • +
  • 合成复用原则:尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现。
  • +
  • 单一职责原则:控制类的粒度大小、将对象解耦、提高其内聚性。
  • +
+
单一职责原则
+
开放封闭原则
+
里氏替换原则
+
依赖倒置原则
+
迪米特原则
+
接口隔离原则
+

创建型模式

+

对类的现实化进行了抽象,能够使软件模块做到与对象的创建和组织无关。

+

功能:类的创建

+
+

单例模式、抽象工厂模式、建造者模式、工厂模式、原型模式

+
+

xx模式

+
应用
+
适用环境
+
优点
+
缺点
+
具体实现
+

xx模式

+
应用
+
适用环境
+
优点
+
缺点
+
具体实现
+

结构型模式

+

描述类和对象之间如何进行有效的组织,以形成良好的软件体系结构,主要的方式是使用继承关系来组织各个类。

+

功能: 组合代替、类与类之间的关系

+
+

适配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模式

+
+

外观模式

+
应用
+
适用环境
+
    +
  • 当要为一个复杂子系统提供一个简单接口时可以使用外观模式。该接口可以满足大多数用户的需求,而且用户也可以越过外观类直接访问子系统。
  • +
  • 客户程序与多个子系统之间存在很大的依赖性。引入外观类将子系统与客户以及其他子系统解耦,可以提高子系统的独立性和可移植性。
  • +
  • 在层次化结构中,可以使用外观模式定义系统中每一层的入口,层与层之间不直接产生联系,而通过外观类建立联系,降低层之间的耦合度。
  • +
+
优点
+
    +
  • 对客户屏蔽子系统组件,减少了客户处理的对象数目并使得子系统使用起来更加容易。通过引入外观模式,客户代码将变得很简单,与之关联的对象也很少。
  • +
  • 实现了子系统与客户之间的松耦合关系,这使得子系统的组件变化不会影响到调用它的客户类,只需要调整外观类即可。
  • +
  • 降低了大型软件系统中的编译依赖性,并简化了系统在不同平台之间的移植过程,因为编译一个子系统一般不需要编译所有其他的子系统。一个子系统的修改对其他子系统没有任何影响,而且子系统内部变化也不会影响到外观对象。
  • +
  • 只是提供了一个访问子系统的统一入口,并不影响用户直接使用子系统类。
  • +
+
缺点
+
    +
  • 不能很好地限制客户使用子系统类,如果对客户访问子系统类做太多的限制则减少了可变性和灵活性。
  • +
  • 在不引入抽象外观类的情况下,增加新的子系统可能需要修改外观类或客户端的源代码,违背了“开闭原则”。
  • +
+
具体实现
+

行为型模式

+

描述 类和对象之间如何交互以及如何分配职责

+

功能:关注对象与行为的分离、就是要把行为分离到类里面

+
+

观察者模式、中介者模式、状态模式、解释器模式、策略模式、职责链模式、访问者模式、迭代器模式、命令模式、迭代器模式、备忘录模式

+
+

命令模式

+
    +
  • 命令模式的本质是对命令进行封装,将发出命令的责任和执行命令的责任分割开。命令模式使请求本身成为一个对象,这个对象和其他对象一样可以被存储和传递
  • +
  • 命令模式的主要优点在于降低系统的耦合度,增加新的命令很方便,而且可以比较容易地设计一个命令队列和宏命令(一次执行多条命令,对命令的批处理),并方便地实现对请求的撤销和恢复;其主要缺点在于可能会导致某些系统有过多的具体命令类。
  • +
  • 命令模式适用情况包括:需要将请求调用者和请求接收者解耦,使得调用者和接收者不直接交互;需要在不同的时间指定请求、将请求排队和执行请求;需要支持命令的撤销操作和恢复操作,需要将一组操作组合在一起,即支持宏命令。
  • +
+
应用
+
    +
  • +

    server和client通信,每条通信消息都带消息号。消息号就是命令传递,该消息是什么消息,有消息号(命令)的消息体(命令对应解析具体的内容)。

    +
  • +
  • +

    手机游戏中玩家每次点击或者手势等操作都是下达一条条命令 (典型) + >屏幕上有多个按钮(每个按钮代表一条命令,更具体些,每个按钮代表一个商品),玩家点击某个按钮,下达某个命令,这个命令是要购买某个商品,对应的响应是一个方法,方法有个参数是商品id,命令就变成把商品id传递到方法内,执行方法

    +
  • +
  • 电视遥控器下达命令
  • +
  • linux平台下的Shell编程 +-计算器加减乘除
  • +
+
适用环境
+
    +
  • 系统需要将请求调用者和请求接收者解耦,使得调用者和接收者不直接交互。
  • +
  • 系统需要在不同的时间指定请求、将请求排队和执行请求。
  • +
  • 系统需要支持命令的撤销(Undo)操作和恢复(Redo)操作。
  • +
  • 系统需要将一组操作组合在一起,即支持宏命令
  • +
+
优点
+
    +
  • 降低系统的耦合度。
  • +
  • 新的命令可以很容易地加入到系统中。
  • +
  • 可以比较容易地设计一个命令队列和宏命令(组合命令)。
  • +
  • 可以方便地实现对请求的Undo和Redo。
  • +
+
缺点
+

使用命令模式可能会导致某些系统有过多的具体命令类。因为针对每一个命令都需要设计一个具体命令类,因此某些系统可能需要大量具体命令类,这将影响命令模式的使用。

+

大型游戏中,server和client通信会有大量的协议定义(每个协议号代表一条命令)

+

中介者模式模式 (非重要)

+
+

中介者模式就是迪米特法则的一个典型应用

+
+

中介者承担两方面的职责:

+
    +
  • 中转作用(结构性):通过中介者提供的中转作用,各个同事对象就不再需要显式引用其他同事,当需要和其他同事进行通信时,通过中介者即可。该中转作用属于中介者在结构上的支持。
  • +
  • 协调作用(行为性):中介者可以更进一步的对同事之间的关系进行封装,同事可以一致地和中介者进行交互,而不需要指明中介者需要具体怎么做,中介者根据封装在自身内部的协调逻辑,对同事的请求进行进一步处理,将同事成员之间的关系行为进行分离和封装。该协调作用属于中介者在行为上的支持。
  • +
+
应用
+
    +
  • 虚拟聊天室(QQ,微信等聊天工具) + >某论坛系统欲增加一个虚拟聊天室,允许论坛会员通过该聊天室进行信息交流,普通会员(CommonMember)可以给其他会员发送文本信息,钻石会员(DiamondMember)既可以给其他会员发送文本信息,还可以发送图片信息。该聊天室可以对不雅字符进行过滤,如“日”等字符;还可以对发送的图片大小进行控制。用中介者模式设计该虚拟聊天室。
  • +
  • MVC架构中控制器 + > Controller 作为一种中介者,它负责控制视图对象View和模型对象Model之间的交互。如在Struts中,Action就可以作为JSP页面与业务对象之间的中介者。
  • +
+
适用环境
+
    +
  • 系统中对象之间存在复杂的引用关系,产生的相互依赖关系结构混乱且难以理解。
  • +
  • 一个对象由于引用了其他很多对象并且直接和这些对象通信,导致难以复用该对象。
  • +
  • 想通过一个中间类来封装多个类中的行为,而又不想生成太多的子类。可以通过引入中介者类来实现,在中介者中定义对象。
  • +
  • 交互的公共行为,如果需要改变行为则可以增加新的中介者类。
  • +
+
优点
+
    +
  • 简化了对象之间的交互。
  • +
  • 将各同事解耦。
  • +
  • 减少子类生成。
  • +
  • 可以简化各同事类的设计和实现。
  • +
+
缺点
+
    +
  • 在具体中介者类中包含了同事之间的交互细节,可能会导致具体中介者类非常复杂,使得系统难以维护。
  • +
+
具体实现
+

中介者模式包含四个角色:抽象中介者用于定义一个接口,该接口用于与各同事对象之间的通信;具体中介者是抽象中介者的子类,通过协调各个同事对象来实现协作行为,了解并维护它的各个同事对象的引用;抽象同事类定义各同事的公有方法;具体同事类是抽象同事类的子类,每一个同事对象都引用一个中介者对象;每一个同事对象在需要和其他同事对象通信时,先与中介者通信,通过中介者来间接完成与其他同事类的通信;在具体同事类中实现了在抽象同事类中定义的方法。

+
+

类似于Control ,拥有各个具体业务对象的引用,进行各个对象的交互控制

+
+

观察者模式

+
    +
  • 观察者模式描述了如何建立对象与对象之间的依赖关系,如何构造满足这种需求的系统。
  • +
  • 这一模式中的关键对象是观察目标和观察者,一个目标可以有任意数目的与之相依赖的观察者,一旦目标的状态发生改变,所有的观察者都将得到通知。
  • +
  • 作为对这个通知的响应,每个观察者都将即时更新自己的状态,以与目标状态同步,这种交互也称为发布-订阅(publishsubscribe)。目标是通知的发布者,它发出通知时并不需要知道谁是它的观察者,可以有任意数目的观察者订阅它并接收通知。
  • +
+
应用
+
    +
  • 数据层和显示层分离,如果显示层V(观察者)关心某个数据M(目标)的更新,则订阅它的通知
  • +
  • 观察者模式在软件开发中应用非常广泛,如某电子商务网站可以在执行发送操作后给用户多个发送商品打折信息。
  • +
  • 某团队战斗游戏中某队友牺牲将给所有成员提示,凡是涉及到一对一或者一对多的对象交互场景都可以使用观察者模式。
  • +
  • 手机游戏中玩家每次点击或者手势等操作都是下达一条条命令(事件id),命令就代表一个事件派发,业务对象(观察者)订阅该事件则会接受到消息 + >屏幕上有多个按钮(每个按钮代表一条命令,更具体些,每个按钮代表一个商品),玩家点击某个按钮,下达某个命令,这个命令是要购买某个商品,对应的响应是一个方法,方法有个参数是商品id,命令就变成把商品id传递到方法内,执行方法(该方法会广播购买事件,谁订阅谁就会收到消息,例如tips提示,例如游戏币展示的ui(单机游戏))
  • +
  • 通知系统(tip等等)
  • +
+
适用环境
+
    +
  • 一个抽象模型有两个方面,其中一个方面依赖于另一个方面。将这些方面封装在独立的对象中使它们可以各自独立地改变和复用。
  • +
  • 一个对象的改变将导致其他一个或多个对象也发生改变,而不知道具体有多少对象将发生改变,可以降低对象之间的耦合度。
  • +
  • 一个对象必须通知其他对象,而并不知道这些对象是谁。
  • +
  • 需要在系统中创建一个触发链,A对象的行为将影响B对象,B对象的行为将影响C对象……,可以使用观察者模式创建一种链式触发机制。
  • +
+
优点
+
    +
  • 观察者模式可以实现表示层和数据逻辑层的分离,并定义了稳定的消息更新传递机制,抽象了更新接口,使得可以有各种各样不同的表示层作为具体观察者角色。
  • +
  • 观察者模式在观察目标和观察者之间建立一个抽象的耦合。
  • +
  • 观察者模式支持广播通信。
  • +
  • 观察者模式符合“开闭原则”的要求。
  • +
+
缺点
+
    +
  • 如果一个观察目标对象有很多直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。
  • +
  • 如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。
  • +
  • 观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。
  • +
+
具体实现
+

状态模式

+

状态模式(State Pattern) :允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的类。其别名为状态对象(Objects for States),状态模式是一种对象行为型模式。

+
应用
+
    +
  • 有限状态机 (典型) + >角色英雄的走,跑,飞行等移动状态,每个角色英雄都有这些状态,这里实现每一个或者每一类角色英雄的每个状态对应的移动速度及动画表现
  • +
  • 状态模式在工作流或游戏等类型的软件中得以广泛使用,甚至可以用于这些系统的核心功能设计,如在政府OA办公系统中,一个批文的状态有多种:尚未办理;正在办理;正在批示;正在审核;已经完成等各种状态,而且批文状态不同时对批文的操作也有所差异。使用状态模式可以描述工作流对象(如批文)的状态转换以及不同状态下它所具有的行为。
  • +
+
适用环境
+
    +
  • 对象的行为依赖于它的状态(属性)并且可以根据它的状态改变而改变它的相关行为。
  • +
  • 代码中包含大量与对象状态有关的条件语句,这些条件语句的出现,会导致代码的可维护性和灵活性变差,不能方便地增加和删除状态,使客户类与类库之间的耦合增强。在这些条件语句中包含了对象的行为,而且这些条件对应于对象的各种状态。
  • +
+
优点
+
    +
  • 封装了转换规则。
  • +
  • 枚举可能的状态,在枚举状态之前需要确定状态种类。
  • +
  • 将所有与某个状态有关的行为放到一个类中,并且可以方便地增加新的状态,只需要改变对象状态即可改变对象的行为。
  • +
  • 允许状态转换逻辑与状态对象合成一体,而不是某一个巨大的条件语句块。
  • +
  • 可以让多个环境对象共享一个状态对象,从而减少系统中对象的个数。
  • +
+
缺点
+
    +
  • 状态模式的使用必然会增加系统类和对象的个数。
  • +
  • 状态模式的结构与实现都较为复杂,如果使用不当将导致程序结构和代码的混乱。
  • +
  • 状态模式对“开闭原则”的支持并不太好,对于可以切换状态的状态模式,增加新的状态类需要修改那些负责状态转换的源代码,否则无法切换到新增状态;而且修改某个状态类的行为也需修改对应类的源代码。
  • +
+
具体实现
+

状态模式包含三个角色:环境类又称为上下文类,它是拥有状态的对象,在环境类中维护一个抽象状态类State的实例,这个实例定义当前状态,在具体实现时,它是一个State子类的对象,可以定义初始状态;抽象状态类用于定义一个接口以封装与环境类的一个特定状态相关的行为;具体状态类是抽象状态类的子类,每一个子类实现一个与环境类的一个状态相关的行为,每一个具体状态类对应环境的一个具体状态,不同的具体状态类其行为有所不同。

+

策略模式

+
    +
  • 如果在一个系统里面有许多类,它们之间的区别仅在于它们的行为,那么使用策略模式可以动态地让一个对象在许多行为中选择一种行为。
  • +
  • 一个系统需要动态地在几种算法中选择一种。
  • +
  • 如果一个对象有很多的行为,如果不用恰当的模式,这些行为就只好使用多重的条件选择语句来实现。
  • +
  • 不希望客户端知道复杂的、与算法相关的数据结构,在具体策略类中封装算法和相关的数据结构,提高算法的保密性与安全性。
  • +
+
应用
+

- 多种算法选择

+
适用环境
+
    +
  • 如果在一个系统里面有许多类,它们之间的区别仅在于它们的行为,那么使用策略模式可以动态地让一个对象在许多行为中选择一种行为。
  • +
  • 一个系统需要动态地在几种算法中选择一种。
  • +
  • 如果一个对象有很多的行为,如果不用恰当的模式,这些行为就只好使用多重的条件选择语句来实现。
  • +
  • 不希望客户端知道复杂的、与算法相关的数据结构,在具体策略类中封装算法和相关的数据结构,提高算法的保密性与安全性。
  • +
+
优点
+
    +
  • 策略模式提供了对“开闭原则”的完美支持,用户可以在不修改原有系统的基础上选择算法或行为,也可以灵活地增加新的算法或行为。
  • +
  • 策略模式提供了管理相关的算法族的办法。
  • +
  • 策略模式提供了可以替换继承关系的办法。
  • +
  • 使用策略模式可以避免使用多重条件转移语句。
  • +
+
缺点
+
    +
  • 客户端必须知道所有的策略类,并自行决定使用哪一个策略类。
  • +
  • 策略模式将造成产生很多策略类,可以通过使用享元模式在一定程度上减少对象的数量。
  • +
+
具体实现
+

策略模式包含三个角色:环境类在解决某个问题时可以采用多种策略,在环境类中维护一个对抽象策略类的引用实例;抽象策略类为所支持的算法声明了抽象方法,是所有策略类的父类;具体策略类实现了在抽象策略类中定义的算法。

+

迭代器模式

+
应用
+
适用环境
+
优点
+
缺点
+
具体实现
+

MVC模式

+

MVC模式是一种架构模式,它包含三个角色:模型(Model),视图(View)和控制器(Controller)。观察者模式可以用来实现MVC模式,观察者模式中的观察目标就是MVC模式中的模型(Model),而观察者就是MVC中的视图(View),控制器(Controller)充当两者之间的中介者(Mediator)。当模型层的数据发生改变时,视图层将自动改变其显示内容。

+
+

引用: +https://design-patterns.readthedocs.io/zh_CN/latest/behavioral_patterns/command.html

+

https://blog.csdn.net/WuLex/article/details/116431864

+

https://blog.csdn.net/jason_jiahongfei/article/details/105108532

+

https://blog.csdn.net/SEU_Calvin/article/details/66994321

+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git "a/ANote/\351\227\256\351\242\230/index.html" "b/ANote/\351\227\256\351\242\230/index.html" new file mode 100644 index 00000000..04935c96 --- /dev/null +++ "b/ANote/\351\227\256\351\242\230/index.html" @@ -0,0 +1,8424 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 问题 - 王国庆的博客 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + 跳转至 + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+ +
+ + + + + + + + + +

问题

+

dll,mdb,pdb说明 +https://gulu-dev.com/post/2016-08-30-unity-external-dll-debugging +http://anferneehardaway.pixnet.net/blog/post/6273453 +http://anferneehardaway.pixnet.net/blog/post/3994168-.pdb-%E7%9A%84%E7%94%A8%E8%99%95

+

蒙皮网格原理

+

哈希算法

+

动画融合

+

登陆id生成

+

shader

+

矩阵

+

网络通信量每秒

+

C#模板类 重命名 +C# GC +ugui +设计模式 +数据结构与算法 +行为树 +有限状态机 +反射和ios拒绝反射有何不同 +C#特性 +异步编程 async await

+
+

mesh转ui绘制后出现遮罩bug,在scrollview中使用时上层增加了一个mask2D遮罩,但是对于scrollview的mesh不生效,20200804

+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git "a/Android/Android-App\345\206\205\346\211\223\345\274\200\350\207\252\345\270\246\346\265\217\350\247\210\345\231\250/index.html" "b/Android/Android-App\345\206\205\346\211\223\345\274\200\350\207\252\345\270\246\346\265\217\350\247\210\345\231\250/index.html" new file mode 100644 index 00000000..669bcd63 --- /dev/null +++ "b/Android/Android-App\345\206\205\346\211\223\345\274\200\350\207\252\345\270\246\346\265\217\350\247\210\345\231\250/index.html" @@ -0,0 +1,8415 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Android-App内打开自带浏览器 - 王国庆的博客 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + 跳转至 + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+ +
+ + + + + + + + + +

Android-App内打开自带浏览器

+



+
import android.net.NetworkInfo;
+import android.net.Uri;
+
+    public void OpenWebView(String url)
+    {
+        Log.i("pay",url);
+        Intent intent = new Intent();
+        intent.setAction("android.intent.action.VIEW");
+        Uri content_url = Uri.parse(url);
+        intent.setData(content_url);
+        intent.setClassName("com.android.browser","com.android.browser.BrowserActivity");
+        startActivity(intent);
+    }
+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git "a/Android/Android\343\200\200\345\210\206\344\272\253App-Icon\351\200\217\346\230\216\345\233\276\347\211\207\345\210\260\345\276\256\344\277\241\345\217\230\351\273\221\347\232\204\351\227\256\351\242\230/index.html" "b/Android/Android\343\200\200\345\210\206\344\272\253App-Icon\351\200\217\346\230\216\345\233\276\347\211\207\345\210\260\345\276\256\344\277\241\345\217\230\351\273\221\347\232\204\351\227\256\351\242\230/index.html" new file mode 100644 index 00000000..3f17dbb5 --- /dev/null +++ "b/Android/Android\343\200\200\345\210\206\344\272\253App-Icon\351\200\217\346\230\216\345\233\276\347\211\207\345\210\260\345\276\256\344\277\241\345\217\230\351\273\221\347\232\204\351\227\256\351\242\230/index.html" @@ -0,0 +1,8455 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Android 分享App-Icon透明图片到微信变黑的问题 - 王国庆的博客 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + 跳转至 + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+ +
+ + + + + + + + + +

Android 分享App-Icon透明图片到微信变黑的问题

+



+
+

参考文章:https://www.cnblogs.com/jasonkent27/p/5113184.html

+
+
/**
+     * bitmap中的透明色用白色替换
+     * 
+     * @param bitmap
+     * @return
+     */
+    public static Bitmap changeColor(Bitmap bitmap) {
+        if (bitmap == null) {
+            return null;
+        }
+        int w = bitmap.getWidth();
+        int h = bitmap.getHeight();
+        int[] colorArray = new int[w * h];
+        int n = 0;
+        for (int i = 0; i < h; i++) {
+            for (int j = 0; j < w; j++) {
+                int color = getMixtureWhite(bitmap.getPixel(j, i));
+                colorArray[n++] = color;
+            }
+        }
+        return Bitmap.createBitmap(colorArray, w, h, Bitmap.Config.ARGB_8888);
+    }
+
+    /**
+     * 获取和白色混合颜色
+     * 
+     * @return
+     */
+    private static int getMixtureWhite(int color) {
+        int alpha = Color.alpha(color);
+        int red = Color.red(color);
+        int green = Color.green(color);
+        int blue = Color.blue(color);
+        return Color.rgb(getSingleMixtureWhite(red, alpha), getSingleMixtureWhite
+
+(green, alpha),
+                getSingleMixtureWhite(blue, alpha));
+    }
+
+    /**
+     * 获取单色的混合值
+     * 
+     * @param color
+     * @param alpha
+     * @return
+     */
+    private static int getSingleMixtureWhite(int color, int alpha) {
+        int newColor = color * alpha / 255 + 255 - alpha;
+        return newColor > 255 ? 255 : newColor;
+    }
+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git "a/Android/Android\357\274\214iOS-\351\200\232\350\277\207\346\265\217\350\247\210\345\231\250\346\211\223\345\274\200app/index.html" "b/Android/Android\357\274\214iOS-\351\200\232\350\277\207\346\265\217\350\247\210\345\231\250\346\211\223\345\274\200app/index.html" new file mode 100644 index 00000000..ad52790d --- /dev/null +++ "b/Android/Android\357\274\214iOS-\351\200\232\350\277\207\346\265\217\350\247\210\345\231\250\346\211\223\345\274\200app/index.html" @@ -0,0 +1,8404 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Android,iOS-通过浏览器打开app - 王国庆的博客 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + 跳转至 + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+ +
+ + + + + + + + + +

Android,iOS-通过浏览器打开app

+



+

https://www.cnblogs.com/sexintercourse/p/5898242.html +Android的android:scheme不要设置成http,否则打不开 +IOS的scheme 在浏览器中用window.location.href=设置有时候没有反应,不知道是不是bug

+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/Android/index.html b/Android/index.html new file mode 100644 index 00000000..2bdca169 --- /dev/null +++ b/Android/index.html @@ -0,0 +1,8454 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Android - 王国庆的博客 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + 跳转至 + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+ + +
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git "a/Graphics/DirectX\347\216\257\345\242\203\346\220\255\345\273\272/index.html" "b/Graphics/DirectX\347\216\257\345\242\203\346\220\255\345\273\272/index.html" new file mode 100644 index 00000000..32b5da2b --- /dev/null +++ "b/Graphics/DirectX\347\216\257\345\242\203\346\220\255\345\273\272/index.html" @@ -0,0 +1,8425 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + DirectX环境搭建 - 王国庆的博客 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + 跳转至 + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+ +
+ + + + + + + + + +

DirectX环境搭建

+
+

IDE支持 DirectX11DirectX12

+
+

vs2019自带了DirectX,所以不要很复杂且繁琐的配置。但是如果安装的时候,没有勾选正确的包,后面就无法找到自带的DirectX。

+
    +
  • 启动Visual Studio Installer
  • +
  • +

    勾选下图所示的两个安装包 + 20210724162525 + 第二个安装包,如果不够选也是无法看到DirectX的。

    +
  • +
  • +

    在通用Windows平台开发中,需要勾选如下所示包 + 20210724162607

    +
  • +
  • 这样在创建项目的时候,就可以看见DirectX11了
    +
  • +
  • 开发者模式也需要打开
    +
  • +
  • 然后,运行一下模板
    +
  • +
+
+

ref: https://juejin.cn/post/6916331857856430088

+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git "a/Graphics/image/DirectX\347\216\257\345\242\203\346\220\255\345\273\272/9RNZmfIWlC2Lsoj.png" "b/Graphics/image/DirectX\347\216\257\345\242\203\346\220\255\345\273\272/9RNZmfIWlC2Lsoj.png" new file mode 100644 index 00000000..a1bdbdf7 --- /dev/null +++ "b/Graphics/image/DirectX\347\216\257\345\242\203\346\220\255\345\273\272/9RNZmfIWlC2Lsoj.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f81c1e63711152c773248af427b061814c92083eb4d23d3597ef2ddbad56f2d3 +size 22820 diff --git "a/Graphics/image/DirectX\347\216\257\345\242\203\346\220\255\345\273\272/G7gmvNHyjq6S419.png" "b/Graphics/image/DirectX\347\216\257\345\242\203\346\220\255\345\273\272/G7gmvNHyjq6S419.png" new file mode 100644 index 00000000..0ae3afd9 --- /dev/null +++ "b/Graphics/image/DirectX\347\216\257\345\242\203\346\220\255\345\273\272/G7gmvNHyjq6S419.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e59d55aeb7fde1f2ccc4c2f63582798c89c48feaf11770c76c045b415e5ad387 +size 56317 diff --git "a/Graphics/image/DirectX\347\216\257\345\242\203\346\220\255\345\273\272/c7nYgPNMuvztEUW.png" "b/Graphics/image/DirectX\347\216\257\345\242\203\346\220\255\345\273\272/c7nYgPNMuvztEUW.png" new file mode 100644 index 00000000..f079e261 --- /dev/null +++ "b/Graphics/image/DirectX\347\216\257\345\242\203\346\220\255\345\273\272/c7nYgPNMuvztEUW.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0d76e7ce3fb1b15b78621c747dc0a4900ec3455a22aa6c58f1f6e02a81a3f842 +size 14350 diff --git "a/Graphics/image/DirectX\347\216\257\345\242\203\346\220\255\345\273\272/cmoCpS2hnw7PDkU.png" "b/Graphics/image/DirectX\347\216\257\345\242\203\346\220\255\345\273\272/cmoCpS2hnw7PDkU.png" new file mode 100644 index 00000000..3684fc69 --- /dev/null +++ "b/Graphics/image/DirectX\347\216\257\345\242\203\346\220\255\345\273\272/cmoCpS2hnw7PDkU.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:159ccda4c2adfcb299fc193aa4e9f724e1cf8e84dab3e55c46f42bedfa16f899 +size 9473 diff --git "a/Graphics/image/DirectX\347\216\257\345\242\203\346\220\255\345\273\272/pd7mfIhNjDC5zVr.png" "b/Graphics/image/DirectX\347\216\257\345\242\203\346\220\255\345\273\272/pd7mfIhNjDC5zVr.png" new file mode 100644 index 00000000..dfb7d532 --- /dev/null +++ "b/Graphics/image/DirectX\347\216\257\345\242\203\346\220\255\345\273\272/pd7mfIhNjDC5zVr.png" @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:083047e93c29e05d6c0a7e95e7aef25c57e262cb391e1c6c07ed5f4b4f4c5a44 +size 14733 diff --git a/Graphics/index.html b/Graphics/index.html new file mode 100644 index 00000000..f6318c63 --- /dev/null +++ b/Graphics/index.html @@ -0,0 +1,8452 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Graphics - 王国庆的博客 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + 跳转至 + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+ +
+ + + + + + + + + +

Graphics

+

这是 Graphics 分类栏目页。

+

已迁移文章

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git "a/IOS/CSharp\345\237\272\347\241\200\347\254\224\350\256\260/index.html" "b/IOS/CSharp\345\237\272\347\241\200\347\254\224\350\256\260/index.html" new file mode 100644 index 00000000..f6214644 --- /dev/null +++ "b/IOS/CSharp\345\237\272\347\241\200\347\254\224\350\256\260/index.html" @@ -0,0 +1,8473 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + CSharp基础笔记 - 王国庆的博客 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + 跳转至 + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+ +
+ + + + + + + + + +

CSharp基础笔记

+



+

变量

+

1.变量的声明和定义

+
+

C#基础:变量的声明、定义、初始化

+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git "a/IOS/IOS\344\270\255Xcode-framework\346\227\240\346\263\225\350\257\206\345\210\253headers\347\233\256\345\275\225\351\227\256\351\242\230\357\274\214\345\244\264\346\226\207\344\273\266\350\257\273\344\270\215\345\207\272\346\235\245/index.html" "b/IOS/IOS\344\270\255Xcode-framework\346\227\240\346\263\225\350\257\206\345\210\253headers\347\233\256\345\275\225\351\227\256\351\242\230\357\274\214\345\244\264\346\226\207\344\273\266\350\257\273\344\270\215\345\207\272\346\235\245/index.html" new file mode 100644 index 00000000..4ff26532 --- /dev/null +++ "b/IOS/IOS\344\270\255Xcode-framework\346\227\240\346\263\225\350\257\206\345\210\253headers\347\233\256\345\275\225\351\227\256\351\242\230\357\274\214\345\244\264\346\226\207\344\273\266\350\257\273\344\270\215\345\207\272\346\235\245/index.html" @@ -0,0 +1,8550 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + IOS中Xcode-framework无法识别headers目录问题,头文件读不出来 - 王国庆的博客 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + 跳转至 + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+ +
+ + + + + + + + + +

IOS中Xcode-framework无法识别headers目录问题,头文件读不出来

+



+
+

引用文章: http://blog.csdn.net/hicui/article/details/51146969

+
+

问题现象

+

framework是ios开发中经常使用到的一个组件,但是有些情况下拿到第三方提供的framework,导入自己的项目后会发现,Headers目录无法识别,编译出错的情况,比如这里:http://tieba.baidu.com/p/4405458569#

+

问题原因

+

常见IOS framework的目录结构为:

+
    +
  • +

    sdk.framework目录 

    +
      +
    • Headers目录
    • +
    • SDK文件
    • +
    +
  • +
+

但是有些第三方framework生成时,脚本有问题,生成的目录结构为:

+
    +
  • +

    sdk.framework目录 

    +
      +
    • Headers软链接
    • +
    • SDK软链接
    • +
    • +

      Versions目录  + -current软链接  + -A目录 

      +
        +
      • Headers目录
      • +
      • SDK文件
      • +
      +
    • +
    +
  • +
+

软链接在不同的电脑上移动时可能会出现路径不存在,因此就导致xcode无法识别Headers目录的情况。

+

解决

+

将sdk.framework/Versions/A/ 下的Headers和SDK文件移动到sdk.framework目录下,将其他文件删除,重新编译即可。

+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git "a/IOS/Xcode\345\267\245\347\250\213\351\205\215\347\275\256/index.html" "b/IOS/Xcode\345\267\245\347\250\213\351\205\215\347\275\256/index.html" new file mode 100644 index 00000000..3086657e --- /dev/null +++ "b/IOS/Xcode\345\267\245\347\250\213\351\205\215\347\275\256/index.html" @@ -0,0 +1,8576 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Xcode工程配置 - 王国庆的博客 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + 跳转至 + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+ +
+
+ + + +
+ +
+ + + + + + + + + +

Xcode工程配置

+



+

0X01导出Xcode工程

+
1.检查热更新配置
+

project_base.json等所有以project_开头的文件的配置信息; +这里的url最后的版本文件要和ios在线的版本一致,不一致则先修改

+

工程配置-1.png

+
2.导出Xcode工程
+

工程配置-2.png

+

工程配置-3.png

+

检查资源版本号和线上的是否相同 +如果是发布版本则点击Publish Packer,如果是热更新资源则点击Publish Bundle +如果选择Publish Packer 发布版本:

+

工程配置-4.png

+

检查界面显示的版本是否为当前线上的版本 +所有版本号加1,然后点击Publish Bundle; +然后导出Xcode工程

+

如果选择Publish Bundler 热更新版本: +检查界面显示的版本是否为当前线上的版本

+

工程配置-5.png

+

清理缓存 +版本号加1,然后点击Publish Bundle; +然后导出Xcode工程

+
3.对Xcode工程进行配置
+

1.打开Capabilities面板 +打开Push Notifications选项

+

工程配置-17.png

+

打开Background Modes选项,勾选Remote notifications

+

工程配置-18.png

+

2.配置打开Notifications后会添加该文件

+

工程配置-19.png

+

这样工程配置完成了,然后开始打包

+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git "a/IOS/Xcode\346\237\245\347\234\213\347\234\237\346\234\272app\346\262\231\347\233\222\345\206\205\345\256\271\357\274\214\346\237\245\347\234\213app\347\274\223\345\255\230\357\274\210Cache\357\274\211\346\226\207\344\273\266/index.html" "b/IOS/Xcode\346\237\245\347\234\213\347\234\237\346\234\272app\346\262\231\347\233\222\345\206\205\345\256\271\357\274\214\346\237\245\347\234\213app\347\274\223\345\255\230\357\274\210Cache\357\274\211\346\226\207\344\273\266/index.html" new file mode 100644 index 00000000..ee078592 --- /dev/null +++ "b/IOS/Xcode\346\237\245\347\234\213\347\234\237\346\234\272app\346\262\231\347\233\222\345\206\205\345\256\271\357\274\214\346\237\245\347\234\213app\347\274\223\345\255\230\357\274\210Cache\357\274\211\346\226\207\344\273\266/index.html" @@ -0,0 +1,8406 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Xcode查看真机app沙盒内容,查看app缓存(Cache)文件 - 王国庆的博客 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + 跳转至 + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+ +
+ + + + + + + + + +

Xcode查看真机app沙盒内容,查看app缓存(Cache)文件

+



+

1.连接你的设备,在Xcode下点击 Window —> Device(cmd + shift + 2) 弹出窗口,选择你的设备,找到你已安装的APP,选中你想要查看沙盒的APP。

+

2.点击底部有个类似设置的按钮,出现几个选项,选择Download Container ,下载文件到本地,将会看到一个后缀为xcappdata的文件,选择这个文件并显示包内容查看对应的沙盒文件。

+

+

*下载失败或者Show Container没有内容 解决方案: 卸载app,重启手机设备,安装app就好了

+

+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git "a/IOS/Xcode\351\241\271\347\233\256\345\217\221\345\270\203App-Store\345\225\206\345\223\201\345\256\241\346\240\270/index.html" "b/IOS/Xcode\351\241\271\347\233\256\345\217\221\345\270\203App-Store\345\225\206\345\223\201\345\256\241\346\240\270/index.html" new file mode 100644 index 00000000..21155678 --- /dev/null +++ "b/IOS/Xcode\351\241\271\347\233\256\345\217\221\345\270\203App-Store\345\225\206\345\223\201\345\256\241\346\240\270/index.html" @@ -0,0 +1,8615 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Xcode项目发布App-Store商品审核 - 王国庆的博客 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + 跳转至 + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ +
+ + + + + + + + + +

Xcode项目发布App-Store商品审核

+



+
+

原文:Xcode项目发布App Store商品审核

+

ios配置开发者证书及添加AppID 参考https://www.jianshu.com/p/ee83dc090b20
+ios 配置ItunesConnect添加应用 参考https://www.jianshu.com/p/12ccfa566ae2
+这里使用Xcode的版本为Version 9.2 (9C40b)

+
+

0X01构建版本

+
    +
  1. 点击Product --> Archive,然后等待构建完成。
  2. +
+

构建版本-1.png

+
    +
  1. 构建完成会打开window==>Organizer对应的界面
  2. +
+

构建版本-2.png

+

构建版本-3.png

+

0X02导出Appstore的ipa包

+
    +
  1. 点击右边的Export==>选择Appstore版本,点击Next
  2. +
+

构建版本-4.png

+
    +
  1. 将勾选去掉,不Upload,点击Next
  2. +
+

构建版本-5.png

+
    +
  1. 选择自动签名,点击Next(如果出现不一样的界面并且左下角有Reset按钮,则点击Reset重试)
  2. +
+

构建版本-6.png

+
    +
  1. 检查信息,点击Export
  2. +
+

构建版本-7.png

+
    +
  1. 选择ipa导出存储路径,点击Export,然后导出ipa完成
  2. +
+

构建版本-8.png

+

0X03上传ipa包到iTunesConnect上,通过ApplicationLoader

+
    +
  1. 打开Application Loader
  2. +
+

构建版本-9.png

+
    +
  1. 配置账户===>点击选取
  2. +
+

构建版本-10.png

+
    +
  1. 选择上面导出的ipa包,点击打开
  2. +
+

构建版本-11.png

+
    +
  1. 点击下一步
  2. +
+

构建版本-12.png

+
    +
  1. 等待上传完成
  2. +
+

构建版本-13.png

+

然后在iTunesConnect上选择刚上传的包

+

0X04导出ipa包用hoc模式(替换上面的第二节0X02)

+
    +
  1. 点击右边的Export==>选择Ad Hoc版本,点击Next
  2. +
+

构建版本-4.png

+
    +
  1. 选择Next
  2. +
+

构建版本-14.png

+
    +
  1. 选择自动签名,点击Next(如果出现不一样的界面并且左下角有Reset按钮,则点击Reset重试)
  2. +
+

构建版本-6.png

+
    +
  1. 检查信息,点击Export
  2. +
+

构建版本-7.png

+
    +
  1. 选择ipa导出存储路径,点击Export,然后导出ipa完成
  2. +
+

构建版本-8.png

+

hoc模式不是上传appstore的,是给自己测试用的

+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git "a/IOS/iOS-11-\345\215\207\347\272\247\345\220\216\346\227\240\346\263\225\347\234\237\346\234\272\350\260\203\350\257\225\357\274\210could-not-locate-device-support-files\357\274\211/index.html" "b/IOS/iOS-11-\345\215\207\347\272\247\345\220\216\346\227\240\346\263\225\347\234\237\346\234\272\350\260\203\350\257\225\357\274\210could-not-locate-device-support-files\357\274\211/index.html" new file mode 100644 index 00000000..ffba9eb4 --- /dev/null +++ "b/IOS/iOS-11-\345\215\207\347\272\247\345\220\216\346\227\240\346\263\225\347\234\237\346\234\272\350\260\203\350\257\225\357\274\210could-not-locate-device-support-files\357\274\211/index.html" @@ -0,0 +1,8410 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + iOS-11-升级后无法真机调试(could-not-locate-device-support-files) - 王国庆的博客 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + 跳转至 + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+ +
+ + + + + + + + + +

iOS-11-升级后无法真机调试(could-not-locate-device-support-files)

+

image.png

+

这个错误是因为Xcode缺少对应ios版本的supoort文件

+
+

iOS 升级到11之后,你会发现无法进行真机测试了。这种情况我在iOS 10.0更新的时候也遇到过。原因是Xcode 的DeviceSupport里面缺少了对应iOS系统版本的SDK。所以你可以选择将Xcode更新到最新版本。

+
+

或者从新版的Xcode目录支持文件复制到自己的Xcode目录中 +文件路径:/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/DeviceSupport +打开这个路径,然后看到ios 系统列表

+

image.png

+

找到自己需要的版本(我用的是11.1)将整个文件夹拷贝到自己的Xcode目录中

+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/IOS/index.html b/IOS/index.html new file mode 100644 index 00000000..5178e8db --- /dev/null +++ b/IOS/index.html @@ -0,0 +1,8462 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + IOS - 王国庆的博客 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + 跳转至 + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + + + + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git "a/IOS/ios-object-c-UIImage\345\233\276\347\211\207\347\274\251\346\224\276/index.html" "b/IOS/ios-object-c-UIImage\345\233\276\347\211\207\347\274\251\346\224\276/index.html" new file mode 100644 index 00000000..d55e4f3e --- /dev/null +++ "b/IOS/ios-object-c-UIImage\345\233\276\347\211\207\347\274\251\346\224\276/index.html" @@ -0,0 +1,8421 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ios-object-c-UIImage图片缩放 - 王国庆的博客 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + 跳转至 + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+ +
+ + + + + + + + + +

ios-object-c-UIImage图片缩放

+



+
1.等比缩放
+
+
+- (UIImage *) scaleImage:(UIImage *)image toScale:(float)scaleSize {
+UIGraphicsBeginImageContext(CGSizeMake(image.size.width * scaleSize, image.size.height * scaleSize);
+[image drawInRect:CGRectMake(0, 0, image.size.width * scaleSize, image.size.height * scaleSize)];
+UIImage *scaledImage = UIGraphicsGetImageFromCurrentImageContext();
+UIGraphicsEndImageContext();
+return scaledImage;
+}
+
+2.自定义大小
+- (UIImage *) reSizeImage:(UIImage *)image toSize:(CGSize)reSize {
+UIGraphicsBeginImageContext(CGSizeMake(reSize.width, reSize.height));
+[image drawInRect:CGRectMake(0, 0, reSize.width, reSize.height)];
+UIImage *reSizeImage = UIGraphicsGetImageFromCurrentImageContext();
+UIGraphicsEndImageContext();
+return reSizeImage;
+}
+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git "a/IOS/ios-object-c-\350\216\267\345\217\226App\345\233\276\346\240\207\344\275\277\347\224\250\347\232\204\345\233\276\347\211\207\345\220\215\345\255\227AppIcon/index.html" "b/IOS/ios-object-c-\350\216\267\345\217\226App\345\233\276\346\240\207\344\275\277\347\224\250\347\232\204\345\233\276\347\211\207\345\220\215\345\255\227AppIcon/index.html" new file mode 100644 index 00000000..521e433e --- /dev/null +++ "b/IOS/ios-object-c-\350\216\267\345\217\226App\345\233\276\346\240\207\344\275\277\347\224\250\347\232\204\345\233\276\347\211\207\345\220\215\345\255\227AppIcon/index.html" @@ -0,0 +1,8411 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ios-object-c-获取App图标使用的图片名字AppIcon - 王国庆的博客 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + 跳转至 + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+ +
+ + + + + + + + + +

ios-object-c-获取App图标使用的图片名字AppIcon

+



+

``` +//获取appIconName +-(NSString)GetAppIconName{ + NSDictionary infoPlist = [[NSBundle mainBundle] infoDictionary];

+
NSString *icon = [[infoPlist valueForKeyPath:@"CFBundleIcons.CFBundlePrimaryIcon.CFBundleIconFiles"] lastObject];
+NSLog(@"GetAppIconName,icon:%@",icon);
+return icon;
+
+

} +···

+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git "a/IOS/ios-\351\205\215\347\275\256ItunesConnect\346\267\273\345\212\240\345\272\224\347\224\250/index.html" "b/IOS/ios-\351\205\215\347\275\256ItunesConnect\346\267\273\345\212\240\345\272\224\347\224\250/index.html" new file mode 100644 index 00000000..865f1068 --- /dev/null +++ "b/IOS/ios-\351\205\215\347\275\256ItunesConnect\346\267\273\345\212\240\345\272\224\347\224\250/index.html" @@ -0,0 +1,8525 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ios-配置ItunesConnect添加应用 - 王国庆的博客 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + 跳转至 + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ +
+ + + + + + + + + +

ios-配置ItunesConnect添加应用

+



+
+

关于怎么配置开发者证书和添加AppID 参考 http://www.jianshu.com/writer#/notebooks/6185424/notes/23554406/preview

+
+

01 在ItunesConnect中添加新的应用

+

1.打开 https://itunesconnect.apple.com/login 并且登录

+

配置iTunesConnect-1.png

+

2.点击我的App

+

配置iTunesConnect-2.png

+

3.点击+====>新建App

+

配置iTunesConnect-3.png

+

4.配置信息

+

配置iTunesConnect-4.png

+

平台:根据需求选择,这里选择iOS
+应用名字
+语言
+套装ID:通过开发者账户配置添加的AppID,参考引用的连接
+SKU :这里我写的bundleID,随意
+点击创建,这样就创建完成

+

02 在ItunesConnect中配置应用的付费信息

+

1.配置应用收费是否收费下载,这里配置是免费

+

配置iTunesConnect-5.png

+

2.添加商品

+

配置iTunesConnect-6.png

+

点击应用的功能===>点击+添加商品

+

配置iTunesConnect-7.png

+

根据自己需求选择添加商品的类型

+

配置iTunesConnect-8.png

+

配置iTunesConnect-9.png

+

配置商品的信息,添加商城截图,完成后点击存储

+

配置iTunesConnect-10.png

+

然后看到该条商品的信息了,状态是准备提交

+

配置iTunesConnect-11.png

+

在提审前打开提审的版本1.0配置界面,向下拉,找到App内购项目,点击+号,然后勾选自己的商品(可以选多个),这里是一小袋钻石,点击完成

+

提审的时候商品也需要审核的!提审的时候商品也需要审核的!提审的时候商品也需要审核的!重要的事情说三遍

+

到这里完成了

+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git "a/IOS/ios\351\205\215\347\275\256\345\274\200\345\217\221\350\200\205\350\257\201\344\271\246\345\217\212\346\267\273\345\212\240AppID/index.html" "b/IOS/ios\351\205\215\347\275\256\345\274\200\345\217\221\350\200\205\350\257\201\344\271\246\345\217\212\346\267\273\345\212\240AppID/index.html" new file mode 100644 index 00000000..b21de254 --- /dev/null +++ "b/IOS/ios\351\205\215\347\275\256\345\274\200\345\217\221\350\200\205\350\257\201\344\271\246\345\217\212\346\267\273\345\212\240AppID/index.html" @@ -0,0 +1,8606 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ios配置开发者证书及添加AppID - 王国庆的博客 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + 跳转至 + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ +
+ + + + + + + + + +

ios配置开发者证书及添加AppID

+



+
+

原文: ios配置开发者证书及添加AppID

+
+

一. 有两种配置开发者证书的方法

+

第一种,通用的并且复杂的一种

+

0X01 创建请求证书的请求文件

+

1.打开钥匙串

+

开发者证书请求文件1.png

+

2

+

开发者证书请求文件2.png

+

开发者证书请求文件3.png

+

2.创建请求文件

+

开发者证书请求文件4.png

+

打开:钥匙串访问===>证书助理===>从证书颁发机构请求证书...

+

开发者证书请求文件5.png

+

请求是:存储到磁盘 +设置电邮地址 +点击继续

+

开发者证书请求文件6.png

+

存储请求文件

+

0X02 创建开发证书

+

1.打开https://developer.apple.com 点击Account 登录

+

[图片上传中...(创建开发者证书2.png-c69c56-1517639491122-0)]
+

+

创建开发者证书2.png

+

创建开发者证书3.png

+

点击:Certificates, Identifiers & Profiles

+

创建开发者证书4.png

+

点击Certificates 的+号,准备创建开发者证书

+

创建开发者证书5.png

+

这里根据自己的需求选择开发证书还是生产证书(如果是自己测试可以选择Development开发证书,如果发布appstore提审,则需要Production生产证书)

+

创建开发者证书6.png

+

拖到屏幕最下面,选择继续

+

创建开发者证书7.png

+

选择继续

+

创建开发者证书8.png

+

这里选择刚才生成的创建开发者证书的请求文件

+

创建开发者证书9.png

+

创建开发者证书10.png

+

选择继续

+

创建开发者证书11.png

+

这里选择下载,证书已经配置完成,下载到本地,然后点击Done

+

证书下载完成后双击自动导入证书,检查证书是否成功导入! +创建开发者证书12.png

+

我之前在创建证书请求文件时配置的常用名称wang_develop 这里看到表示成功了

+

第二种,通过Xcode配置,(我这里是Xcode9.2,Xcode8.0及以上都可以)

+

1.打开Xcode,这里就不说怎么打开了 +2.点击左上角Xcode按钮===>Preferences...

+

创建开发者证书21.png

+
    +
  1. 点击 Accounts 选择自己apple账户,然后点击 * Manage Certificates*
  2. +
+

创建开发者证书22.png

+

4.点击+号

+

创建开发者证书23.png

+

这里点击+号后展开菜单有Development和App Store(这里根据自己的需求选择开发证书还是生产证书(如果是自己测试可以选择Development开发证书,如果发布appstore提审,则需要App Store生产证书)) +等待证书添加完成

+

创建开发者证书24.png

+

这里证书添加完成,然后重启Xcode,防止出现未知bug

+

二. 添加AppId

+

打开开发者账户网站(上面网页中配置证书的地方) +1.App IDs==>+号 +选择左侧的appIds

+

配置开发者AppID-1.png

+

2.

+

配置开发者AppID-2.png

+

添加AppId的名字和BundleID +点击继续,(在屏幕最下面) +3.选择选项点击继续

+

配置开发者AppID-3.png

+

4.点击Register完成

+

配置开发者AppID-4.png

+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git "a/IOS/xcode\346\216\245sdk\346\227\266\346\212\245\351\224\231Undefined-symbols-for-architecture-arm64-/index.html" "b/IOS/xcode\346\216\245sdk\346\227\266\346\212\245\351\224\231Undefined-symbols-for-architecture-arm64-/index.html" new file mode 100644 index 00000000..b909eec1 --- /dev/null +++ "b/IOS/xcode\346\216\245sdk\346\227\266\346\212\245\351\224\231Undefined-symbols-for-architecture-arm64-/index.html" @@ -0,0 +1,8418 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + xcode接sdk时报错Undefined-symbols-for-architecture-arm64- - 王国庆的博客 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + 跳转至 + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+ +
+ + + + + + + + + +

xcode接sdk时报错Undefined-symbols-for-architecture-arm64-

+



+

unity 导出xcode后接环信libHyphenateSDK时build报错Undefined symbols for architecture armv7: +也有Undefined symbols for architecture arm64:

+
Undefined symbols for architecture armv7:
+  "_CGImageDestinationCreateWithURL", referenced from:
+      -[EMVideoMessageBody initWithLocalPath:displayName:] in libHyphenateSDK.a(EMVideoMessageBody.o)
+  "_CGImageDestinationAddImage", referenced from:
+      -[EMVideoMessageBody initWithLocalPath:displayName:] in libHyphenateSDK.a(EMVideoMessageBody.o)
+  "_CGImageDestinationFinalize", referenced from:
+      -[EMVideoMessageBody initWithLocalPath:displayName:] in libHyphenateSDK.a(EMVideoMessageBody.o)
+ld: symbol(s) not found for architecture armv7
+clang: error: linker command failed with exit code 1 (use -v to see invocation)
+
+

+

这个错误以为sdk或者framework不支持armv7或者arm64,我这实际上是缺少framework +这个错误是工程里面没有引用 ImageIO.framework造成的,

+

+

环信的libHyphenateSDK.a库依赖ImageIO.framework,结果报错让人无语

+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git "a/MkDocs\345\215\232\345\256\242\346\226\271\346\241\210/index.html" "b/MkDocs\345\215\232\345\256\242\346\226\271\346\241\210/index.html" new file mode 100644 index 00000000..107e87bd --- /dev/null +++ "b/MkDocs\345\215\232\345\256\242\346\226\271\346\241\210/index.html" @@ -0,0 +1,9155 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + MkDocs 博客方案 - 王国庆的博客 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + 跳转至 + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + + + + + +
+ +
+ + + + + + + + + +

MkDocs 博客方案

+

本文档用于说明当前博客从 Hugo 迁移到 MkDocs 之后的标准使用方式,包括本地环境部署、日常写作、主题选择、GitHub Pages 部署、gh-pages 分支使用方式,以及搜索、评论、Git 更新时间等增强能力方案。

+

2. 本地环境部署

+

2.1 Python 环境

+

建议使用 Python 3.10 及以上版本。

+

检查版本:

+
python --version
+
+

2.2 安装 MkDocs 和主题

+

当前站点使用 Material for MkDocs 主题。

+

安装命令:

+
pip install mkdocs mkdocs-material
+
+

2.3 安装推荐插件

+

如果要支持 Git 更新时间、作者信息等功能,建议同时安装以下插件:

+
pip install mkdocs-git-revision-date-localized-plugin mkdocs-git-authors-plugin gitpython jieba
+
+

说明:

+
    +
  • mkdocs-git-revision-date-localized-plugin:显示页面最后更新时间、创建时间
  • +
  • mkdocs-git-authors-plugin:显示页面作者信息
  • +
  • gitpython:部分 Git 相关插件依赖
  • +
  • jieba:增强中文搜索分词
  • +
+

2.4 本地启动预览

+

在项目根目录执行:

+
mkdocs serve
+
+

默认访问地址:

+
http://127.0.0.1:8000
+
+

2.5 本地构建静态站点

+
mkdocs build
+
+

构建产物默认输出到:

+
site/
+
+

title: 我的随笔 +date: 2026-03-21 +categories: + - ANote +tags: + - 随笔 + - 记录 +status: published

+
+

我的随笔

+

背景

+

记录一下今天的想法。

+

正文

+

正文内容。

+

总结

+

一句话总结。

+

+说明:
+
+- 文件名尽量简洁,必要时可带日期
+- `ANote` 比较适合杂记、随笔、草稿整理、阶段记录
+- 如果后续启用标签插件,上述 front matter 可以直接复用
+
+### 3.3 如何添加分类
+
+MkDocs 没有 Hugo 那种内建 taxonomy 分类体系,当前项目推荐使用:
+
+## 目录即分类
+
+例如:
+
+- `docs/ANote/`
+- `docs/u3d/`
+- `docs/shader/`
+- `docs/other/`
+
+分类的标准做法:
+
+1. 在 `docs/` 下创建目录
+2. 在目录中放一个 `index.md` 作为栏目首页
+3. 在 `mkdocs.yml` 的 `nav` 中加入该目录
+4. 将该类文章都放在此目录下
+
+例如新增一个 `essay` 分类:
+
+```text
+docs/essay/
+docs/essay/index.md
+
+

index.md 示例:

+
# essay
+
+这里收录个人随笔、阶段总结与非技术长文。
+
+

然后在 mkdocs.yml 中添加:

+
- essay:
+  - 栏目首页: essay/index.md
+
+

3.4 如何添加标签

+

当前项目可以先采用“文档中写标签 + 后续逐步增强”的方式。

+

推荐在文章头部添加 front matter:

+
---
+title: Shader 学习记录
+tags:
+  - Shader
+  - Unity
+  - 图形学
+categories:
+  - shader
+---
+
+

当前阶段标签主要有两个用途:

+
    +
  1. 作为文章元数据保留
  2. +
  3. 为后续接入标签插件或博客插件做准备
  4. +
+

如果后续要把标签真正展示成可点击页面,有两种方案:

+

方案 A:接入博客/标签插件

+

优点:自动化程度高。
+缺点:配置复杂度更高。

+

方案 B:手工维护标签索引页

+

例如建立:

+
docs/tags/index.md
+
+

手动按标签整理文章链接。

+

优点:简单稳定。
+缺点:需要人工维护。

+

对于你当前项目,建议先采用:

+

先写 front matter 标签,展示层后续再增强

+
+

4. 主题如何使用

+

当前站点已经使用:

+
    +
  • Material for MkDocs
  • +
+

mkdocs.yml 中基本配置形式如下:

+
theme:
+  name: material
+  language: zh
+
+

4.1 常用能力

+

Material 主题常用能力包括:

+
    +
  • 全文搜索
  • +
  • 左侧导航树
  • +
  • 深色/浅色模式
  • +
  • 代码高亮
  • +
  • 提示块(admonition)
  • +
  • 页内目录
  • +
  • 顶部导航增强
  • +
  • 页脚与社交链接
  • +
+

4.2 常见扩展配置示例

+
theme:
+  name: material
+  language: zh
+  features:
+    - navigation.tabs
+    - navigation.sections
+    - navigation.expand
+    - navigation.top
+    - search.suggest
+    - search.highlight
+    - content.code.copy
+
+

4.3 推荐开启的 Markdown 扩展

+
markdown_extensions:
+  - admonition
+  - tables
+  - toc:
+      permalink: true
+  - pymdownx.highlight
+  - pymdownx.superfences
+  - pymdownx.inlinehilite
+  - pymdownx.tabbed:
+      alternate_style: true
+
+
+

5. 推荐哪些主题

+

5.1 第一推荐:Material for MkDocs

+

适用场景:

+
    +
  • 技术博客
  • +
  • 知识库
  • +
  • 文档站
  • +
  • 带导航分组的大型文章集合
  • +
+

优点:

+
    +
  • 功能最全
  • +
  • 社区成熟
  • +
  • 搜索体验好
  • +
  • 主题美观
  • +
  • 插件与生态完善
  • +
+

5.2 次选:MkDocs 默认主题

+

适用场景:

+
    +
  • 极简站点
  • +
  • 个人内部笔记
  • +
  • 对美观要求不高,只追求最小依赖
  • +
+

优点:

+
    +
  • 轻量
  • +
  • 配置最简单
  • +
+

缺点:

+
    +
  • 展示能力弱
  • +
  • 不适合你当前这种多分类博客
  • +
+

5.3 其他主题建议

+

如果后续想尝试更博客化的风格,可以考虑:

+
    +
  • Material + blog 插件
  • +
  • Material + 自定义首页
  • +
+

对于当前项目,不建议频繁换主题,建议继续以 Material 为主。

+
+

6. 如何部署到 GitHub

+

6.1 部署目标说明

+

推荐方式:

+
    +
  • master:源码分支
  • +
  • gh-pages:静态站点发布分支
  • +
+

GitHub Pages 读取 gh-pages 分支内容进行站点发布。

+

6.2 gh-pages 分支如何使用

+

gh-pages 是专门用来放静态网站产物的分支。

+

它的特点:

+
    +
  • 不保存 MkDocs 源码
  • +
  • 只保存构建好的 HTML、CSS、JS、图片等文件
  • +
  • 每次部署时会被新的构建产物覆盖
  • +
+

因此:

+
    +
  • 日常开发不要在 gh-pages 上改文章
  • +
  • 日常开发永远在 master 上进行
  • +
  • gh-pages 只作为发布分支使用
  • +
+

6.3 当前在 master 分支,如何手工将产物推送到 gh-pages

+

最简单方式:

+
mkdocs gh-deploy --clean
+
+

这个命令会自动:

+
    +
  1. 构建站点
  2. +
  3. 切换发布逻辑
  4. +
  5. 将产物推送到 gh-pages
  6. +
+

如果要强制覆盖远端:

+
mkdocs gh-deploy --clean --force
+
+

6.4 手工发布的底层逻辑说明

+

如果不使用 mkdocs gh-deploy,理论上也可以手工发布:

+
    +
  1. master 执行 mkdocs build
  2. +
  3. 得到 site/ 目录
  4. +
  5. 切换到 gh-pages
  6. +
  7. 清空旧内容
  8. +
  9. site/ 内容复制到分支根目录
  10. +
  11. 提交并推送
  12. +
+

这种方式可行,但不推荐长期使用,因为容易出错。

+

6.5 GitHub Pages 仓库设置

+

在 GitHub 仓库页面中:

+
    +
  1. 打开 Settings
  2. +
  3. 打开 Pages
  4. +
  5. 如果使用 Actions 自动化:选择 GitHub Actions
  6. +
  7. 如果使用分支发布:选择 Deploy from a branch
  8. +
  9. 分支选择 gh-pages
  10. +
+

推荐优先使用:

+

GitHub Actions 自动部署

+
+

7. 是否使用 GitHub Actions 自动化

+

7.1 当前仓库已有自动化部署

+

当前仓库已经存在工作流:

+
.github/workflows/deploy-mkdocs.yml
+
+

当前逻辑是:

+
    +
  • master 分支有新的 push 时触发
  • +
  • 自动安装 MkDocs 依赖
  • +
  • 执行 mkdocs gh-deploy --force --clean
  • +
  • 自动推送到 gh-pages
  • +
+

7.2 当前工作流的意义

+

这意味着:

+
    +
  • 你平时只需要提交 master
  • +
  • 不需要手工切换 gh-pages
  • +
  • 不需要把 site/ 提交到源码分支
  • +
+

7.3 当前工作流示意

+
name: Deploy MkDocs to GitHub Pages
+
+on:
+  push:
+    branches:
+      - master
+
+permissions:
+  contents: write
+
+jobs:
+  deploy:
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v4
+        with:
+          fetch-depth: 0
+
+      - uses: actions/setup-python@v5
+        with:
+          python-version: "3.x"
+
+      - name: Install dependencies
+        run: |
+          pip install mkdocs-material mkdocs-git-revision-date-localized-plugin gitpython jieba
+
+      - name: Deploy
+        run: mkdocs gh-deploy --force --clean
+
+

7.4 自动化部署推荐结论

+

对于当前项目,推荐:

+

保持 master 维护源码,使用 GitHub Actions 自动发布到 gh-pages

+

这是最省心、最稳定的方式。

+
+

8. master 分支如何忽略 MkDocs 产物

+

MkDocs 默认构建输出目录为:

+
site/
+
+

这个目录不应该提交到 master 分支。

+

因此必须在 .gitignore 中加入:

+
site/
+
+

这样可以避免:

+
    +
  • 构建产物污染源码分支
  • +
  • 出现大量无意义 diff
  • +
  • 误把静态 HTML 提交到 master
  • +
+

推荐原则:

+
    +
  • master 只放源码
  • +
  • gh-pages 只放构建产物
  • +
+
+

9. 博客增强能力方案

+

9.1 搜索(保留 jieba 依赖)

+

当前站点继续使用 Material 主题自带搜索能力,推荐在 mkdocs.yml 中显式保留:

+
plugins:
+  - search
+
+

如果中文内容较多,建议继续保留 jieba 依赖,用于增强中文分词搜索效果。

+

安装示例:

+
pip install jieba
+
+

如果使用 GitHub Actions 自动部署,也应在工作流安装依赖时保留 jieba

+
pip install mkdocs-material mkdocs-git-revision-date-localized-plugin mkdocs-git-authors-plugin gitpython jieba
+
+

推荐结论:

+
    +
  • 搜索功能保留
  • +
  • jieba 依赖保留
  • +
  • 继续使用 Material 内置搜索,不额外引入第三方搜索服务
  • +
+

9.2 显示 Git 最后更新时间

+

推荐使用插件:

+
pip install mkdocs-git-revision-date-localized-plugin
+
+

推荐配置:

+
plugins:
+  - search
+  - git-revision-date-localized:
+      enable_creation_date: true
+      type: datetime
+      timezone: Asia/Shanghai
+      locale: zh
+
+

这个插件可以提供:

+
    +
  • 页面最后更新时间
  • +
  • 页面创建时间(首次提交时间)
  • +
+

适合技术博客和知识库,因为读者可以快速判断文档是否过时。

+

9.3 显示 Git 作者信息

+

推荐使用插件:

+
pip install mkdocs-git-authors-plugin
+
+

推荐配置:

+
plugins:
+  - search
+  - git-revision-date-localized:
+      enable_creation_date: true
+      type: datetime
+      timezone: Asia/Shanghai
+      locale: zh
+  - git-authors
+
+

作用:

+
    +
  • 显示页面作者信息
  • +
  • 多人协作时可追踪主要维护者
  • +
+

对于单人博客,它的价值主要体现在:

+
    +
  • 为后续多人协作留好扩展位
  • +
  • 与 Git 更新时间一起构成完整的文档元信息
  • +
+

9.4 支持评论系统:采用 Giscus 方案

+

评论系统建议统一采用:

+

Giscus

+

原因:

+
    +
  • 基于 GitHub Discussions
  • +
  • 不需要自建评论后端
  • +
  • 与 GitHub Pages 兼容性好
  • +
  • 适合技术博客
  • +
  • 评论数据跟随仓库生态,不依赖额外数据库
  • +
+

接入步骤:

+
    +
  1. 在 GitHub 仓库中开启 Discussions
  2. +
  3. 访问 https://giscus.app/zh-CN
  4. +
  5. 选择仓库、Discussion 分类、映射方式
  6. +
  7. 生成脚本配置
  8. +
  9. 通过 Material 自定义模板或额外 HTML 注入到文章页
  10. +
+

建议映射方式:

+
    +
  • 使用页面路径或页面标题作为 discussions 映射键
  • +
+

推荐结论:

+
    +
  • 评论系统统一使用 Giscus
  • +
  • 不再推荐新增 Disqus 之类的重型第三方方案
  • +
  • Utterances 作为备选,但优先级低于 Giscus
  • +
+

9.5 支持标签系统

+

当前项目已经建议在文章 front matter 中保留标签字段,例如:

+
---
+title: Shader 学习记录
+tags:
+  - Shader
+  - Unity
+  - 图形学
+categories:
+  - shader
+---
+
+

推荐的标签系统落地分两步:

+

第一步:先统一写标签元数据

+

所有新文章都尽量补充 tags 字段,先把标签数据沉淀下来。

+

优点:

+
    +
  • 成本低
  • +
  • 不影响当前文档结构
  • +
  • 为后续自动化标签页做准备
  • +
+

第二步:再决定标签展示方式

+

推荐两种可选方案:

+
方案 A:手工维护标签索引页
+

例如建立:

+
docs/tags/index.md
+
+

再按标签整理文章入口。

+

优点:

+
    +
  • 最稳定
  • +
  • 不依赖额外插件
  • +
  • 易于控制展示质量
  • +
+

缺点:

+
    +
  • 需要人工维护
  • +
+
方案 B:后续接入博客/标签插件
+

适合未来更博客化的展示需求,例如:

+
    +
  • 自动生成标签归档页
  • +
  • 标签聚合浏览
  • +
  • 更像博客站点而非纯文档站
  • +
+

优点:

+
    +
  • 自动化程度高
  • +
+

缺点:

+
    +
  • 配置复杂度更高
  • +
  • 需要额外适配当前目录结构
  • +
+

当前项目推荐结论:

+

先统一写 tags 元数据,展示层先采用手工标签页,后续再评估是否插件化

+

9.6 显示 Git 提交更新日志

+

MkDocs 默认不直接展示每篇文章的完整 Git 提交历史。

+

推荐分两档:

+

最低成本方案

+
    +
  • 显示最后更新时间
  • +
  • 显示作者信息
  • +
  • 增加“编辑此页”入口
  • +
  • 通过 GitHub 页面查看历史提交
  • +
+

优点:

+
    +
  • 实现简单
  • +
  • 维护成本低
  • +
  • 对当前博客最合适
  • +
+

增强方案

+

在 CI 中通过脚本执行 git log,自动生成一份全站或每篇文章的 changelog 页面,例如:

+
docs/changelog/index.md
+
+

优点:

+
    +
  • 可在站内直接看更新历史
  • +
+

缺点:

+
    +
  • 维护复杂度高
  • +
  • 需要定制脚本
  • +
+

当前推荐先采用:

+

最低成本方案

+
+

10. 推荐的后续配置增强

+

后续建议逐步增强 mkdocs.yml

+
theme:
+  name: material
+  language: zh
+  features:
+    - navigation.tabs
+    - navigation.sections
+    - navigation.expand
+    - navigation.top
+    - search.suggest
+    - search.highlight
+    - content.code.copy
+
+plugins:
+  - search
+  - git-revision-date-localized:
+      enable_creation_date: true
+      type: datetime
+      timezone: Asia/Shanghai
+      locale: zh
+  - git-authors
+
+

如果要支持“编辑此页”:

+
repo_url: <你的 GitHub 仓库地址>
+edit_uri: edit/master/docs/
+
+

这样每篇文章都能直接跳转到 GitHub 编辑页面。

+
+

11. 推荐维护规范

+

11.1 文件命名

+

建议:

+
    +
  • 文件名直接用文章标题即可
  • +
  • 图片放在对应分类目录下的 image/文章名/
  • +
  • 不要把大量公共图片散落在根目录
  • +
+

11.2 分类原则

+

建议分类保持稳定,不要频繁改目录名。

+

11.3 标签原则

+

建议标签数量控制在 3 到 5 个以内,避免标签泛滥。

+

11.4 部署原则

+
    +
  • 平时只改 master
  • +
  • 构建产物不提交到 master
  • +
  • 通过 GitHub Actions 自动发到 gh-pages
  • +
+
+

12. 当前项目推荐的最终落地方案

+

对于当前博客,最推荐的组合是:

+
    +
  • 主题:Material for MkDocs
  • +
  • 内容组织:目录即分类
  • +
  • 标签:先写 front matter,后续按需展示
  • +
  • 搜索:启用 search
  • +
  • 更新时间:启用 mkdocs-git-revision-date-localized-plugin
  • +
  • 提交历史:先使用 GitHub 页面查看,后续再决定是否生成 changelog 页面
  • +
  • 评论:使用 Giscus
  • +
  • 部署:master 保存源码,GitHub Actions 自动部署到 gh-pages
  • +
  • 忽略:master 分支忽略 site/
  • +
+

这套方案最适合当前仓库的结构,也最容易长期维护。

+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/README.md b/README.md deleted file mode 100644 index 68abbd13..00000000 --- a/README.md +++ /dev/null @@ -1,279 +0,0 @@ -# 王国庆的博客 - -这是个人技术博客的源码仓库,同时也可以作为博客的基础阅读版使用。 - -- **高级版在线阅读**:[https://codingriver.github.io/](https://codingriver.github.io/) -- **基础版仓库入口**:当前仓库首页与 `docs/` 目录 - ---- - -## 📚 完整文章索引 - - -### Android 开发 - -- **2020-12-13** - [Android-App内打开自带浏览器](docs/Android/Android-App内打开自带浏览器.md) -- **2020-12-13** - [Android 分享App-Icon透明图片到微信变黑的问题](docs/Android/Android 分享App-Icon透明图片到微信变黑的问题.md) -- **2020-12-13** - [Android,iOS-通过浏览器打开app](docs/Android/Android,iOS-通过浏览器打开app.md) - -### C# 语言与工程 - -- **2022-06-30** - [IL指令集](docs/csharp/IL指令集.md) -- **2022-03-30** - [俩问号和值类型加问号分别是什么意思](docs/csharp/俩问号和值类型加问号分别是什么意思.md) -- **2021-12-20** - [CSharp中Struct内存大小](docs/csharp/CSharp中Struct内存大小.md) -- **2021-04-07** - [C# 特性详解(Attribute)](docs/csharp/CSharp的特性详解.md) -- **2021-04-07** - [SynchronizationContext线程上下文简单说明](docs/csharp/SynchronizationContext线程上下文简单说明.md) -- **2021-04-07** - [C# async与await的使用说明](docs/csharp/async与await的使用说明.md) -- **2021-04-07** - [new关键字的几种用法](docs/csharp/new关键字的几种用法.md) -- **2021-04-07** - [字符串比较效率测试](docs/csharp/字符串比较效率测试.md) -- **2021-04-07** - [闭包Closure](docs/csharp/闭包Closure.md) -- **2020-12-13** - [C#-Unicode编码和中文互转](docs/csharp/CSharp-Unicode编码和中文互转.md) -- **2020-12-13** - [CSharp用Process类调用cmd执行及命令行首字母乱码问题](docs/csharp/CSharp用Process类调用cmd执行及命令行首字母乱码问题.md) -- **2020-12-13** - [CSharp语言6-字符串插值](docs/csharp/CSharp语言6-字符串插值.md) -- **2020-12-13** - [Enum_Int_String的互相转换](docs/csharp/Enum_Int_String的互相转换.md) -- **2020-12-13** - [中文和unicode互转](docs/csharp/中文和unicode互转.md) -- **2020-12-13** - [客户端开发CSharp命名规范手册](docs/csharp/客户端开发CSharp命名规范手册.md) - -### Lua 语言 - -- **2021-04-07** - [lua中类的实现](docs/lua/lua中类的实现.md) -- **2021-04-07** - [lua闭包和and-or操作符及其它的说明](docs/lua/lua闭包和and-or操作符及其它的说明.md) - -### Shader 与图形学 - -- **2024-03-01** - [Gamma和Linear及sRGB说明](docs/shader/Gamma和Linear及sRGB说明.md) -- **2022-03-06** - [泛光(Bloom)实现](docs/shader/Bloom实现.md) -- **2021-12-22** - [ShaderLearn-(一)漫反射](docs/shader/ShaderLearn-(一)漫反射.md) -- **2021-12-22** - [ShaderLearn-(三)RampTex](docs/shader/ShaderLearn-(三)RampTex.md) -- **2021-12-22** - [ShaderLearn-(二)镜面反射](docs/shader/ShaderLearn-(二)镜面反射.md) -- **2021-12-22** - [Shader光照模型-漫反射](docs/shader/Shader光照模型-漫反射.md) -- **2021-12-22** - [Shader光照模型-高光反射](docs/shader/Shader光照模型-高光反射.md) -- **2021-12-22** - [边缘光和流光特效](docs/shader/边缘光和流光特效.md) -- **2021-12-22** - [边缘光特效](docs/shader/边缘光特效.md) -- **2021-12-20** - [Shader学习-1.1基础概要](docs/shader/Shader学习-1.1基础概要.md) -- **2021-12-20** - [【Shader学习】前言](docs/shader/Shader学习-前言.md) -- **2021-12-20** - [模糊实现](docs/shader/模糊实现.md) -- **2021-11-23** - [Shader消融特效](docs/shader/Shader消融特效.md) -- **2021-08-15** - [【Shader】常用函数(UnityShader内置函数、CG 和 GLSL 内置函数等)和内置变量](docs/shader/UnityShader常用函数.md) -- **2021-04-07** - [Shader水波纹效果](docs/shader/Shader水波纹效果.md) -- **2021-04-07** - [Shader纹理动画](docs/shader/Shader纹理动画.md) -- **2020-12-13** - [Shader法线纹理](docs/shader/Shader法线纹理.md) -- **2020-12-13** - [Shader法线贴图](docs/shader/Shader法线贴图.md) -- **2020-12-13** - [Shader法线贴图(世界空间)](docs/shader/Shader法线贴图(世界空间).md) - -### Unity 工具 - -- **2021-12-20** - [ByteHelper字节数组辅助工具](docs/unityTool/ByteHelper字节数组辅助工具.md) -- **2021-12-20** - [JsonHelper工具](docs/unityTool/JsonHelper工具.md) -- **2021-12-20** - [StringHelper编码辅助工具](docs/unityTool/StringHelper编码辅助工具.md) -- **2021-12-20** - [生成分布式唯一ID](docs/unityTool/生成分布式唯一ID.md) -- **2021-12-20** - [遍历打印对象的所有变量和属性](docs/unityTool/遍历打印对象的所有变量和属性.md) -- **2021-04-18** - [Unity-Assert断言工具](docs/unityTool/Unity-Assert断言工具.md) -- **2020-12-13** - [Md5计算工具](docs/unityTool/Md5计算工具.md) -- **2020-12-13** - [ProfilerHelper辅助工具](docs/unityTool/ProfilerHelper辅助工具.md) -- **2020-12-13** - [URLHelper-URL编码解码工具](docs/unityTool/URLHelper-URL编码解码工具.md) -- **2020-12-13** - [ZipHelper压缩工具](docs/unityTool/ZipHelper压缩工具.md) - -### Unity 开发 - -- **2026-03-20** - [世界部队DrawCall分析和优化](docs/u3d/世界部队DrawCall分析和优化.md) -- **2025-10-13** - [DrawCall优化实战](docs/u3d/DrawCall优化实战.md) -- **2023-12-19** - [RenderDoc-调试](docs/u3d/RenderDoc-调试.md) -- **2022-04-29** - [unity性能优化总结](docs/u3d/unity性能优化总结.md) -- **2022-04-24** - [Unity脚本执行顺序](docs/u3d/Unity脚本执行顺序.md) -- **2022-04-24** - [unity脚本MonoBehaviour默认方法执行顺序测试](docs/u3d/unity脚本MonoBehaviour默认方法执行顺序测试.md) -- **2022-03-09** - [Unity渲染顺序](docs/u3d/Unity渲染顺序.md) -- **2022-03-06** - [xlua性能测试](docs/u3d/xlua性能测试.md) -- **2021-12-24** - [Unity-性能优化相关知识(转)](docs/u3d/Unity-性能优化相关知识(转).md) -- **2021-12-20** - [UGUI-Text换行问题](docs/u3d/UGUI-Text换行问题.md) -- **2021-12-20** - [Unity-Texture2D的SetPiexl严重耗时的优化方案](docs/u3d/Unity-Texture2D的SetPiexl严重耗时的优化方案.md) -- **2021-12-20** - [Unity平台宏定义常用说明](docs/u3d/Unity平台宏定义常用说明.md) -- **2021-12-20** - [unity矩阵变换从模型空间到屏幕空间的转换](docs/u3d/unity矩阵变换从模型空间到屏幕空间的转换.md) -- **2021-08-15** - [c++_structunion数据对齐](docs/u3d/c++_structunion数据对齐.md) -- **2021-07-08** - [UnityProfiler使用Android真机连接测试](docs/u3d/UnityProfiler使用Android真机连接测试.md) -- **2021-04-07** - [UGUI-Image图片置灰](docs/u3d/UGUI-Image图片置灰.md) -- **2021-04-07** - [UGUI优化-使用镜像图片](docs/u3d/UGUI优化-使用镜像图片.md) -- **2021-04-07** - [unity-脚本设置AssetBundleName和Variant](docs/u3d/unity-脚本设置AssetBundleName和Variant.md) -- **2021-04-07** - [xLua-第三方扩展的添加和编译](docs/u3d/xLua-第三方扩展的添加和编译.md) -- **2021-04-07** - [修改Prefab实例将Transform变为RectTransform](docs/u3d/修改Prefab实例将Transform变为RectTransform.md) -- **2020-12-13** - [CSharp命名规范手册](docs/u3d/CSharp命名规范手册.md) -- **2020-12-13** - [UGUI中CustomFont(BMFont)字体使用与制作](docs/u3d/UGUI中CustomFont(BMFont)字体使用与制作.md) -- **2020-12-13** - [UGUI事件介绍](docs/u3d/UGUI事件介绍.md) -- **2020-12-13** - [UGUI事件系统](docs/u3d/UGUI事件系统.md) -- **2020-12-13** - [UGUI使用富文本RichText](docs/u3d/UGUI使用富文本RichText.md) -- **2020-12-13** - [UGUI运用美术字体](docs/u3d/UGUI运用美术字体.md) -- **2020-12-13** - [Unity Editor和Player日志文件的存放](docs/u3d/Unity Editor和Player日志文件的存放.md) -- **2020-12-13** - [Unity-GameObject-销毁(Destroy)后的几种状态](docs/u3d/Unity-GameObject-销毁(Destroy)后的几种状态.md) -- **2020-12-13** - [Unity-使用Android-Splash](docs/u3d/Unity-使用Android-Splash.md) -- **2020-12-13** - [Unity-日志文件的存放](docs/u3d/Unity-日志文件的存放.md) -- **2020-12-13** - [UnityEditor-iOS-Xcode报错](docs/u3d/UnityEditor-iOS-Xcode报错.md) -- **2020-12-13** - [Unity中获取依赖信息出现其他未引用字体问题](docs/u3d/Unity中获取依赖信息出现其他未引用字体问题.md) -- **2020-12-13** - [Unity切入后台,切入前台,获取焦点,失去焦点等](docs/u3d/Unity切入后台,切入前台,获取焦点,失去焦点等.md) -- **2020-12-13** - [Unity判断机型是否为iPhoneX等](docs/u3d/Unity判断机型是否为iPhoneX等.md) -- **2020-12-13** - [Unity加载场景](docs/u3d/Unity加载场景.md) -- **2020-12-13** - [Unity和IOS交互char-作为方法返回值到CSharp中bug](docs/u3d/Unity和IOS交互char-作为方法返回值到CSharp中bug.md) -- **2020-12-13** - [Unity截屏](docs/u3d/Unity截屏.md) -- **2020-12-13** - [Unity粒子特效缩放](docs/u3d/Unity粒子特效缩放.md) -- **2020-12-13** - [unity--android获取电量和wifi信号强度](docs/u3d/unity--android获取电量和wifi信号强度.md) -- **2020-12-13** - [unity-3DCamera根据宽度适配(3D相机适配)](docs/u3d/unity-3DCamera根据宽度适配(3D相机适配).md) -- **2020-12-13** - [unity-Font-Test测试](docs/u3d/unity-Font-Test测试.md) -- **2020-12-13** - [unity-ios审核ipv6](docs/u3d/unity-ios审核ipv6.md) -- **2020-12-13** - [unity-响应Android返回键](docs/u3d/unity-响应Android返回键.md) -- **2020-12-13** - [unity-打包Android报错CommandInvokationFailure--Unable-to-convert-classes-i](docs/u3d/unity-打包Android报错CommandInvokationFailure--Unable-to-convert-classes-i.md) -- **2020-12-13** - [unity-模型显示锯齿问题](docs/u3d/unity-模型显示锯齿问题.md) -- **2020-12-13** - [unity3d中的LayerMask用法](docs/u3d/unity3d中的LayerMask用法.md) -- **2020-12-13** - [unity优化-相同模型的多个实例设置各自的颜色优化](docs/u3d/unity优化-相同模型的多个实例设置各自的颜色优化.md) -- **2020-12-13** - [unity创建新脚本自动添加版权声明,文件名,作者,创建时间等](docs/u3d/unity创建新脚本自动添加版权声明,文件名,作者,创建时间等.md) -- **2020-12-13** - [unity发布android版本-so静态库打不进去](docs/u3d/unity发布android版本-so静态库打不进去.md) -- **2020-12-13** - [unity和android交互bug](docs/u3d/unity和android交互bug.md) -- **2020-12-13** - [unity接入声网sdk视频bug](docs/u3d/unity接入声网sdk视频bug.md) -- **2020-12-13** - [unity禁止屏幕休眠](docs/u3d/unity禁止屏幕休眠.md) -- **2020-12-13** - [unity设置屏幕分辨率](docs/u3d/unity设置屏幕分辨率.md) -- **2020-12-13** - [unity读取StreamingAssets文本文件](docs/u3d/unity读取StreamingAssets文本文件.md) -- **2020-12-13** - [untiy-ios透明ui-的透明区域渲染错误](docs/u3d/untiy-ios透明ui-的透明区域渲染错误.md) -- **2020-12-13** - [使用TexturePacker命令行的一个坑](docs/u3d/使用TexturePacker命令行的一个坑.md) -- **2020-12-13** - [苹果内购](docs/u3d/苹果内购.md) - -### Unity 编辑器扩展 - -- **2022-03-09** - [MeshRenderer在Inspector设置SorttingLayerName和SorttingOrder](docs/unity-editor/MeshRenderer在Inspector设置SorttingLayerName和SorttingOrder.md) -- **2021-12-20** - [SerializedObject获取所有属性](docs/unity-editor/SerializedObject获取所有属性.md) -- **2021-04-07** - [Unity编辑器扩展菜单项](docs/unity-editor/Unity编辑器扩展菜单项.md) -- **2020-12-13** - [unity编辑器-自定义Inspector面板](docs/unity-editor/unity编辑器-自定义Inspector面板.md) - -### iOS 开发 - -- **2021-12-20** - [Xcode查看真机app沙盒内容,查看app缓存(Cache)文件](docs/IOS/Xcode查看真机app沙盒内容,查看app缓存(Cache)文件.md) -- **2021-12-20** - [xcode接sdk时报错Undefined-symbols-for-architecture-arm64-](docs/IOS/xcode接sdk时报错Undefined-symbols-for-architecture-arm64-.md) -- **2020-12-13** - [CSharp基础笔记](docs/IOS/CSharp基础笔记.md) -- **2020-12-13** - [IOS中Xcode-framework无法识别headers目录问题,头文件读不出来](docs/IOS/IOS中Xcode-framework无法识别headers目录问题,头文件读不出来.md) -- **2020-12-13** - [Xcode工程配置](docs/IOS/Xcode工程配置.md) -- **2020-12-13** - [Xcode项目发布App-Store商品审核](docs/IOS/Xcode项目发布App-Store商品审核.md) -- **2020-12-13** - [iOS-11-升级后无法真机调试(could-not-locate-device-support-files)](docs/IOS/iOS-11-升级后无法真机调试(could-not-locate-device-support-files).md) -- **2020-12-13** - [ios-object-c-UIImage图片缩放](docs/IOS/ios-object-c-UIImage图片缩放.md) -- **2020-12-13** - [ios-object-c-获取App图标使用的图片名字AppIcon](docs/IOS/ios-object-c-获取App图标使用的图片名字AppIcon.md) -- **2020-12-13** - [ios-配置ItunesConnect添加应用](docs/IOS/ios-配置ItunesConnect添加应用.md) -- **2020-12-13** - [ios配置开发者证书及添加AppID](docs/IOS/ios配置开发者证书及添加AppID.md) - -### 图形学 - -- **2021-12-20** - [DirectX环境搭建](docs/Graphics/DirectX环境搭建.md) - -### 工具与系统 - -- **2024-03-15** - [Nodejs初始化配置](docs/other/nodejs初始化配置.md) -- **2022-08-29** - [六种常用设计模式](docs/other/六种常用设计模式.md) -- **2021-12-27** - [三次握手和四次挥手](docs/other/三次握手和四次挥手.md) -- **2021-04-07** - [Json的float单精度浮点数类型支持](docs/other/Json的float单精度浮点数类型支持.md) -- **2021-04-07** - [Sourcetree基本使用教程](docs/other/Sourcetree基本使用教程.md) -- **2021-04-07** - [Sourcetree安装教程(windows)](docs/other/Sourcetree安装教程(windows).md) -- **2021-04-07** - [Windows创建点开头的文件或者点开头的文件夹](docs/other/Windows创建点开头的文件或者点开头的文件夹.md) -- **2021-04-07** - [mac设置Library 显示](docs/other/mac设置Library 显示.md) -- **2021-04-07** - [win10-iis-http建站](docs/other/win10-iis-http建站.md) -- **2021-04-07** - [添加网络位置(共享目录)](docs/other/添加网络位置(共享目录).md) -- **2021-04-07** - [申请TexturePacker免费注册码(2018年11月份申请的)](docs/other/申请TexturePacker免费注册码(2018年11月份申请的).md) -- **2020-12-13** - [3500个常用汉字(字体裁剪用)](docs/other/3500个常用汉字(字体裁剪用).md) -- **2020-12-13** - [GitHub-ssh失败Permission-denied-(publickey)](docs/other/GitHub-ssh失败Permission-denied-(publickey).md) -- **2020-12-13** - [LuaForWindows-安装报错](docs/other/LuaForWindows-安装报错.md) -- **2020-12-13** - [MAC-创建-IPv6-WIFI热点,不用以太网接口](docs/other/MAC-创建-IPv6-WIFI热点,不用以太网接口.md) -- **2020-12-13** - [SVN-trunk(主线)-branch(分支)-创建和合并教程](docs/other/SVN-trunk(主线)-branch(分支)-创建和合并教程.md) -- **2020-12-13** - [SVN无法提交--a和--so文件](docs/other/SVN无法提交--a和--so文件.md) -- **2020-12-13** - [Sourcetree-Mac安装跳过注册登陆和选择服务器](docs/other/Sourcetree-Mac安装跳过注册登陆和选择服务器.md) -- **2020-12-13** - [VS2013+LUA代码高亮配置教程](docs/other/VS2013+LUA代码高亮配置教程.md) -- **2020-12-13** - [VS2013创建新脚本自动添加版权声明,文件名,作者,创建时间等](docs/other/VS2013创建新脚本自动添加版权声明,文件名,作者,创建时间等.md) -- **2020-12-13** - [Xlua-线程问题](docs/other/Xlua-线程问题.md) -- **2020-12-13** - [linux gcc版本如何升级到gcc4.8.2](docs/other/linux gcc版本如何升级到gcc4.8.2.md) -- **2020-12-13** - [tar(child)-lbzip2-Cannotexec解决方法](docs/other/tar(child)-lbzip2-Cannotexec解决方法.md) -- **2020-12-13** - [win7安装Vs2015失败的解决方案(安装vs多个版本的问题)](docs/other/win7安装Vs2015失败的解决方案(安装vs多个版本的问题).md) -- **2020-12-13** - [用astash 画UML类图](docs/other/用astash 画UML类图.md) - -### 数学与图形学基础 - -- **2024-03-13** - [透视校正差值](docs/math/透视校正差值.md) -- **2021-12-20** - [LinearEyeDepth推导](docs/math/LinearEyeDepth推导.md) -- **2021-12-20** - [屏幕空间变换](docs/math/屏幕空间变换.md) -- **2021-12-20** - [法线变换矩阵推导](docs/math/法线变换矩阵推导.md) -- **2021-12-20** - [点乘和叉乘的计算和应用](docs/math/点乘和叉乘的计算和应用.md) -- **2021-12-20** - [透视投影变换推导](docs/math/透视投影变换推导.md) -- **2021-12-20** - [齐次坐标的意义](docs/math/齐次坐标的意义.md) -- **2021-08-15** - [两个平面矩形相交(不旋转)](docs/math/两个平面矩形相交(不旋转).md) -- **2021-04-07** - [三角函数和差公式的推导](docs/math/三角函数和差公式的推导.md) -- **2021-04-07** - [基本求导法则与导数公式](docs/math/基本求导法则与导数公式.md) -- **2021-04-07** - [微分和积分数学公式大全](docs/math/微分和积分数学公式大全.md) -- **2020-12-13** - [线性代数的本质](docs/math/线性代数的本质.md) - -### 数据结构与算法 - -- **2021-04-07** - [AVL树详解](docs/datastruct/AVL树详解.md) -- **2021-04-07** - [A星算法理解](docs/datastruct/A星算法理解.md) -- **2021-04-07** - [二叉查找树](docs/datastruct/二叉查找树.md) -- **2021-04-07** - [伸展树详解](docs/datastruct/伸展树详解.md) -- **2021-04-07** - [十大经典排序算法及比较与分析](docs/datastruct/十大经典排序算法及比较与分析.md) -- **2021-04-07** - [树和二叉树基本讲解](docs/datastruct/树和二叉树基本讲解.md) - -### 数据结构基础 - -- **2021-04-07** - [浮点数的内存存储和运算](docs/datastructbase/浮点数的内存存储和运算.md) - -### 系统基础 - -- **2021-12-20** - [ASCII,Unicode和UTF-8](docs/basesystem/ASCII,Unicode和UTF-8.md) -- **2021-04-07** - [多字节大小端说明](docs/basesystem/多字节大小端说明.md) -- **2021-04-07** - [深入理解计算机系统第二章部分练习题](docs/basesystem/深入理解计算机系统第二章部分练习题.md) -- **2020-12-13** - [URL编码与解码原理](docs/basesystem/URL编码与解码原理.md) -- **2020-12-13** - [为什么用补码,为什么有符号单字节最小是-128](docs/basesystem/为什么用补码,为什么有符号单字节最小是-128.md) -- **2020-12-13** - [位操作符介绍](docs/basesystem/位操作符介绍.md) -- **2020-12-13** - [位运算实现加减乘除](docs/basesystem/位运算实现加减乘除.md) -- **2020-12-13** - [十进制和二进制互相转换](docs/basesystem/十进制和二进制互相转换.md) -- **2020-12-13** - [原码、反码、补码和移码详解](docs/basesystem/原码、反码、补码和移码详解.md) - -### 项目记录与随笔 - -- **2026-03-21** - [web开发笔记](docs/ANote/web开发笔记.md) -- **2025-12-23** - [日常笔记](docs/ANote/日常笔记.md) -- **2025-02-06** - [SSH笔记](docs/ANote/SSH笔记.md) -- **2024-07-22** - [NAS笔记](docs/ANote/NAS笔记.md) -- **2024-07-18** - [cloudflare反向代理工具](docs/ANote/cloudflare反向代理工具.md) -- **2024-07-05** - [Shell笔记](docs/ANote/Shell笔记.md) -- **2024-03-13** - [UI笔记](docs/ANote/UI笔记.md) -- **2024-01-26** - [Linux笔记](docs/ANote/Linux笔记.md) -- **2023-12-19** - [DrawCall优化实战演讲稿](docs/ANote/DrawCall优化实战演讲稿.md) -- **2023-08-14** - [DOTS笔记](docs/ANote/DOTS笔记.md) -- **2023-07-28** - [jenkins记录](docs/ANote/jenkins记录.md) -- **2023-04-27** - [IGG开发笔记](docs/ANote/IGG开发笔记.md) -- **2023-04-03** - [IGG性能分析笔记](docs/ANote/IGG性能分析笔记.md) -- **2023-01-05** - [EOC项目笔记](docs/ANote/EOC项目笔记.md) -- **2022-10-11** - [视频处理](docs/ANote/视频处理.md) -- **2022-09-01** - [设计模式笔记](docs/ANote/设计模式笔记.md) -- **2022-07-05** - [CSharp笔记](docs/ANote/CSharp笔记.md) -- **2022-06-27** - [EOC项目分析笔记1](docs/ANote/EOC项目分析笔记1.md) -- **2022-06-24** - [梳理](docs/ANote/梳理.md) -- **2022-03-30** - [Git笔记](docs/ANote/Git笔记.md) -- **2022-03-24** - [Shader案例](docs/ANote/Shader案例.md) -- **2022-03-24** - [Shader笔记2](docs/ANote/Shader笔记2.md) -- **2022-03-24** - [shader记录](docs/ANote/shader记录.md) -- **2022-03-10** - [SRPBatcher和GPUInstance](docs/ANote/SRPBatcher和GPUInstance.md) -- **2022-03-09** - [Shader笔记](docs/ANote/Shader笔记.md) -- **2022-03-09** - [unity编辑器笔记](docs/ANote/unity编辑器笔记.md) -- **2022-03-09** - [包体大小优化笔记](docs/ANote/包体大小优化笔记.md) -- **2022-03-06** - [PBR笔记](docs/ANote/PBR笔记.md) -- **2022-03-06** - [ParticleEffectForUGUI使用教程](docs/ANote/ParticleEffectForUGUI使用教程.md) -- **2022-03-06** - [golang笔记](docs/ANote/golang笔记.md) -- **2022-03-06** - [操作系统笔记](docs/ANote/操作系统笔记.md) -- **2022-03-06** - [日常使用命令](docs/ANote/日常使用命令.md) -- **2022-03-06** - [矩阵笔记](docs/ANote/矩阵笔记.md) -- **2022-02-12** - [caddy笔记](docs/ANote/caddy笔记.md) -- **2021-12-22** - [Unity-URP笔记](docs/ANote/Unity-URP笔记.md) -- **2021-12-20** - [00-Test](docs/ANote/00-Test.md) -- **2021-08-15** - [CSharp记录](docs/ANote/CSharp记录.md) -- **2021-08-15** - [Unity记录](docs/ANote/Unity记录.md) -- **2021-08-15** - [其他记录](docs/ANote/其他记录.md) -- **2021-08-15** - [问题](docs/ANote/问题.md) -- **2020-12-13** - [2018-09-26](docs/ANote/2018-09-26.md) -- **2020-12-13** - [socket测试结果](docs/ANote/socket测试结果.md) -- **2020-12-13** - [记录](docs/ANote/记录.md) - -### 项目记录与随笔 - -详见 [ANote 栏目](docs/ANote/index.md) - diff --git a/about/index.html b/about/index.html new file mode 100644 index 00000000..5c5598b5 --- /dev/null +++ b/about/index.html @@ -0,0 +1,8489 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 关于 - 王国庆的博客 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + 跳转至 + + +
+
+ +
+ + + + +
+ + +
+ +
+ + + + + + + + + +
+
+ + + +
+
+
+ + + + + + + +
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+ +
+ + + + + + + + + +

关于

+

你好,这里是王国庆的个人技术博客。

+

本站主要记录这些内容:

+
    +
  • Unity 客户端开发
  • +
  • Shader 与图形学基础
  • +
  • C# 语言与工程实践
  • +
  • Android / iOS 平台接入经验
  • +
  • 日常开发笔记与工具链整理
  • +
+

写作方式

+

文章以长期积累的项目经验、学习笔记和问题排查记录为主,尽量做到:

+
    +
  • 保留可复用结论
  • +
  • 保留关键代码与截图
  • +
  • 保留踩坑过程中的上下文
  • +
+

当前状态

+

本站已完成从 Hugo 到 MkDocs 的主体迁移,后续会继续优化:

+
    +
  • 标签体系
  • +
  • 栏目首页内容
  • +
  • 历史文章元数据质量
  • +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+ + + +
+ + + +
+
+
+
+ + + + + + + + + + + + + \ No newline at end of file diff --git a/assets/images/favicon.png b/assets/images/favicon.png new file mode 100644 index 00000000..1cf13b9f Binary files /dev/null and b/assets/images/favicon.png differ diff --git a/assets/javascripts/bundle.79ae519e.min.js b/assets/javascripts/bundle.79ae519e.min.js new file mode 100644 index 00000000..3df3e5e6 --- /dev/null +++ b/assets/javascripts/bundle.79ae519e.min.js @@ -0,0 +1,16 @@ +"use strict";(()=>{var Zi=Object.create;var _r=Object.defineProperty;var ea=Object.getOwnPropertyDescriptor;var ta=Object.getOwnPropertyNames,Bt=Object.getOwnPropertySymbols,ra=Object.getPrototypeOf,Ar=Object.prototype.hasOwnProperty,bo=Object.prototype.propertyIsEnumerable;var ho=(e,t,r)=>t in e?_r(e,t,{enumerable:!0,configurable:!0,writable:!0,value:r}):e[t]=r,P=(e,t)=>{for(var r in t||(t={}))Ar.call(t,r)&&ho(e,r,t[r]);if(Bt)for(var r of Bt(t))bo.call(t,r)&&ho(e,r,t[r]);return e};var vo=(e,t)=>{var r={};for(var o in e)Ar.call(e,o)&&t.indexOf(o)<0&&(r[o]=e[o]);if(e!=null&&Bt)for(var o of Bt(e))t.indexOf(o)<0&&bo.call(e,o)&&(r[o]=e[o]);return r};var Cr=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports);var oa=(e,t,r,o)=>{if(t&&typeof t=="object"||typeof t=="function")for(let n of ta(t))!Ar.call(e,n)&&n!==r&&_r(e,n,{get:()=>t[n],enumerable:!(o=ea(t,n))||o.enumerable});return e};var $t=(e,t,r)=>(r=e!=null?Zi(ra(e)):{},oa(t||!e||!e.__esModule?_r(r,"default",{value:e,enumerable:!0}):r,e));var go=(e,t,r)=>new Promise((o,n)=>{var i=c=>{try{a(r.next(c))}catch(p){n(p)}},s=c=>{try{a(r.throw(c))}catch(p){n(p)}},a=c=>c.done?o(c.value):Promise.resolve(c.value).then(i,s);a((r=r.apply(e,t)).next())});var xo=Cr((kr,yo)=>{(function(e,t){typeof kr=="object"&&typeof yo!="undefined"?t():typeof define=="function"&&define.amd?define(t):t()})(kr,(function(){"use strict";function e(r){var o=!0,n=!1,i=null,s={text:!0,search:!0,url:!0,tel:!0,email:!0,password:!0,number:!0,date:!0,month:!0,week:!0,time:!0,datetime:!0,"datetime-local":!0};function a(k){return!!(k&&k!==document&&k.nodeName!=="HTML"&&k.nodeName!=="BODY"&&"classList"in k&&"contains"in k.classList)}function c(k){var ut=k.type,je=k.tagName;return!!(je==="INPUT"&&s[ut]&&!k.readOnly||je==="TEXTAREA"&&!k.readOnly||k.isContentEditable)}function p(k){k.classList.contains("focus-visible")||(k.classList.add("focus-visible"),k.setAttribute("data-focus-visible-added",""))}function l(k){k.hasAttribute("data-focus-visible-added")&&(k.classList.remove("focus-visible"),k.removeAttribute("data-focus-visible-added"))}function f(k){k.metaKey||k.altKey||k.ctrlKey||(a(r.activeElement)&&p(r.activeElement),o=!0)}function u(k){o=!1}function d(k){a(k.target)&&(o||c(k.target))&&p(k.target)}function v(k){a(k.target)&&(k.target.classList.contains("focus-visible")||k.target.hasAttribute("data-focus-visible-added"))&&(n=!0,window.clearTimeout(i),i=window.setTimeout(function(){n=!1},100),l(k.target))}function S(k){document.visibilityState==="hidden"&&(n&&(o=!0),X())}function X(){document.addEventListener("mousemove",ee),document.addEventListener("mousedown",ee),document.addEventListener("mouseup",ee),document.addEventListener("pointermove",ee),document.addEventListener("pointerdown",ee),document.addEventListener("pointerup",ee),document.addEventListener("touchmove",ee),document.addEventListener("touchstart",ee),document.addEventListener("touchend",ee)}function re(){document.removeEventListener("mousemove",ee),document.removeEventListener("mousedown",ee),document.removeEventListener("mouseup",ee),document.removeEventListener("pointermove",ee),document.removeEventListener("pointerdown",ee),document.removeEventListener("pointerup",ee),document.removeEventListener("touchmove",ee),document.removeEventListener("touchstart",ee),document.removeEventListener("touchend",ee)}function ee(k){k.target.nodeName&&k.target.nodeName.toLowerCase()==="html"||(o=!1,re())}document.addEventListener("keydown",f,!0),document.addEventListener("mousedown",u,!0),document.addEventListener("pointerdown",u,!0),document.addEventListener("touchstart",u,!0),document.addEventListener("visibilitychange",S,!0),X(),r.addEventListener("focus",d,!0),r.addEventListener("blur",v,!0),r.nodeType===Node.DOCUMENT_FRAGMENT_NODE&&r.host?r.host.setAttribute("data-js-focus-visible",""):r.nodeType===Node.DOCUMENT_NODE&&(document.documentElement.classList.add("js-focus-visible"),document.documentElement.setAttribute("data-js-focus-visible",""))}if(typeof window!="undefined"&&typeof document!="undefined"){window.applyFocusVisiblePolyfill=e;var t;try{t=new CustomEvent("focus-visible-polyfill-ready")}catch(r){t=document.createEvent("CustomEvent"),t.initCustomEvent("focus-visible-polyfill-ready",!1,!1,{})}window.dispatchEvent(t)}typeof document!="undefined"&&e(document)}))});var ro=Cr((jy,Rn)=>{"use strict";/*! + * escape-html + * Copyright(c) 2012-2013 TJ Holowaychuk + * Copyright(c) 2015 Andreas Lubbe + * Copyright(c) 2015 Tiancheng "Timothy" Gu + * MIT Licensed + */var qa=/["'&<>]/;Rn.exports=Ka;function Ka(e){var t=""+e,r=qa.exec(t);if(!r)return t;var o,n="",i=0,s=0;for(i=r.index;i{/*! + * clipboard.js v2.0.11 + * https://clipboardjs.com/ + * + * Licensed MIT © Zeno Rocha + */(function(t,r){typeof Nt=="object"&&typeof io=="object"?io.exports=r():typeof define=="function"&&define.amd?define([],r):typeof Nt=="object"?Nt.ClipboardJS=r():t.ClipboardJS=r()})(Nt,function(){return(function(){var e={686:(function(o,n,i){"use strict";i.d(n,{default:function(){return Xi}});var s=i(279),a=i.n(s),c=i(370),p=i.n(c),l=i(817),f=i.n(l);function u(q){try{return document.execCommand(q)}catch(C){return!1}}var d=function(C){var _=f()(C);return u("cut"),_},v=d;function S(q){var C=document.documentElement.getAttribute("dir")==="rtl",_=document.createElement("textarea");_.style.fontSize="12pt",_.style.border="0",_.style.padding="0",_.style.margin="0",_.style.position="absolute",_.style[C?"right":"left"]="-9999px";var D=window.pageYOffset||document.documentElement.scrollTop;return _.style.top="".concat(D,"px"),_.setAttribute("readonly",""),_.value=q,_}var X=function(C,_){var D=S(C);_.container.appendChild(D);var N=f()(D);return u("copy"),D.remove(),N},re=function(C){var _=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{container:document.body},D="";return typeof C=="string"?D=X(C,_):C instanceof HTMLInputElement&&!["text","search","url","tel","password"].includes(C==null?void 0:C.type)?D=X(C.value,_):(D=f()(C),u("copy")),D},ee=re;function k(q){"@babel/helpers - typeof";return typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?k=function(_){return typeof _}:k=function(_){return _&&typeof Symbol=="function"&&_.constructor===Symbol&&_!==Symbol.prototype?"symbol":typeof _},k(q)}var ut=function(){var C=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{},_=C.action,D=_===void 0?"copy":_,N=C.container,G=C.target,We=C.text;if(D!=="copy"&&D!=="cut")throw new Error('Invalid "action" value, use either "copy" or "cut"');if(G!==void 0)if(G&&k(G)==="object"&&G.nodeType===1){if(D==="copy"&&G.hasAttribute("disabled"))throw new Error('Invalid "target" attribute. Please use "readonly" instead of "disabled" attribute');if(D==="cut"&&(G.hasAttribute("readonly")||G.hasAttribute("disabled")))throw new Error(`Invalid "target" attribute. You can't cut text from elements with "readonly" or "disabled" attributes`)}else throw new Error('Invalid "target" value, use a valid Element');if(We)return ee(We,{container:N});if(G)return D==="cut"?v(G):ee(G,{container:N})},je=ut;function R(q){"@babel/helpers - typeof";return typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?R=function(_){return typeof _}:R=function(_){return _&&typeof Symbol=="function"&&_.constructor===Symbol&&_!==Symbol.prototype?"symbol":typeof _},R(q)}function se(q,C){if(!(q instanceof C))throw new TypeError("Cannot call a class as a function")}function ce(q,C){for(var _=0;_0&&arguments[0]!==void 0?arguments[0]:{};this.action=typeof N.action=="function"?N.action:this.defaultAction,this.target=typeof N.target=="function"?N.target:this.defaultTarget,this.text=typeof N.text=="function"?N.text:this.defaultText,this.container=R(N.container)==="object"?N.container:document.body}},{key:"listenClick",value:function(N){var G=this;this.listener=p()(N,"click",function(We){return G.onClick(We)})}},{key:"onClick",value:function(N){var G=N.delegateTarget||N.currentTarget,We=this.action(G)||"copy",Yt=je({action:We,container:this.container,target:this.target(G),text:this.text(G)});this.emit(Yt?"success":"error",{action:We,text:Yt,trigger:G,clearSelection:function(){G&&G.focus(),window.getSelection().removeAllRanges()}})}},{key:"defaultAction",value:function(N){return Mr("action",N)}},{key:"defaultTarget",value:function(N){var G=Mr("target",N);if(G)return document.querySelector(G)}},{key:"defaultText",value:function(N){return Mr("text",N)}},{key:"destroy",value:function(){this.listener.destroy()}}],[{key:"copy",value:function(N){var G=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{container:document.body};return ee(N,G)}},{key:"cut",value:function(N){return v(N)}},{key:"isSupported",value:function(){var N=arguments.length>0&&arguments[0]!==void 0?arguments[0]:["copy","cut"],G=typeof N=="string"?[N]:N,We=!!document.queryCommandSupported;return G.forEach(function(Yt){We=We&&!!document.queryCommandSupported(Yt)}),We}}]),_})(a()),Xi=Ji}),828:(function(o){var n=9;if(typeof Element!="undefined"&&!Element.prototype.matches){var i=Element.prototype;i.matches=i.matchesSelector||i.mozMatchesSelector||i.msMatchesSelector||i.oMatchesSelector||i.webkitMatchesSelector}function s(a,c){for(;a&&a.nodeType!==n;){if(typeof a.matches=="function"&&a.matches(c))return a;a=a.parentNode}}o.exports=s}),438:(function(o,n,i){var s=i(828);function a(l,f,u,d,v){var S=p.apply(this,arguments);return l.addEventListener(u,S,v),{destroy:function(){l.removeEventListener(u,S,v)}}}function c(l,f,u,d,v){return typeof l.addEventListener=="function"?a.apply(null,arguments):typeof u=="function"?a.bind(null,document).apply(null,arguments):(typeof l=="string"&&(l=document.querySelectorAll(l)),Array.prototype.map.call(l,function(S){return a(S,f,u,d,v)}))}function p(l,f,u,d){return function(v){v.delegateTarget=s(v.target,f),v.delegateTarget&&d.call(l,v)}}o.exports=c}),879:(function(o,n){n.node=function(i){return i!==void 0&&i instanceof HTMLElement&&i.nodeType===1},n.nodeList=function(i){var s=Object.prototype.toString.call(i);return i!==void 0&&(s==="[object NodeList]"||s==="[object HTMLCollection]")&&"length"in i&&(i.length===0||n.node(i[0]))},n.string=function(i){return typeof i=="string"||i instanceof String},n.fn=function(i){var s=Object.prototype.toString.call(i);return s==="[object Function]"}}),370:(function(o,n,i){var s=i(879),a=i(438);function c(u,d,v){if(!u&&!d&&!v)throw new Error("Missing required arguments");if(!s.string(d))throw new TypeError("Second argument must be a String");if(!s.fn(v))throw new TypeError("Third argument must be a Function");if(s.node(u))return p(u,d,v);if(s.nodeList(u))return l(u,d,v);if(s.string(u))return f(u,d,v);throw new TypeError("First argument must be a String, HTMLElement, HTMLCollection, or NodeList")}function p(u,d,v){return u.addEventListener(d,v),{destroy:function(){u.removeEventListener(d,v)}}}function l(u,d,v){return Array.prototype.forEach.call(u,function(S){S.addEventListener(d,v)}),{destroy:function(){Array.prototype.forEach.call(u,function(S){S.removeEventListener(d,v)})}}}function f(u,d,v){return a(document.body,u,d,v)}o.exports=c}),817:(function(o){function n(i){var s;if(i.nodeName==="SELECT")i.focus(),s=i.value;else if(i.nodeName==="INPUT"||i.nodeName==="TEXTAREA"){var a=i.hasAttribute("readonly");a||i.setAttribute("readonly",""),i.select(),i.setSelectionRange(0,i.value.length),a||i.removeAttribute("readonly"),s=i.value}else{i.hasAttribute("contenteditable")&&i.focus();var c=window.getSelection(),p=document.createRange();p.selectNodeContents(i),c.removeAllRanges(),c.addRange(p),s=c.toString()}return s}o.exports=n}),279:(function(o){function n(){}n.prototype={on:function(i,s,a){var c=this.e||(this.e={});return(c[i]||(c[i]=[])).push({fn:s,ctx:a}),this},once:function(i,s,a){var c=this;function p(){c.off(i,p),s.apply(a,arguments)}return p._=s,this.on(i,p,a)},emit:function(i){var s=[].slice.call(arguments,1),a=((this.e||(this.e={}))[i]||[]).slice(),c=0,p=a.length;for(c;c0&&i[i.length-1])&&(p[0]===6||p[0]===2)){r=0;continue}if(p[0]===3&&(!i||p[1]>i[0]&&p[1]=e.length&&(e=void 0),{value:e&&e[o++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")}function K(e,t){var r=typeof Symbol=="function"&&e[Symbol.iterator];if(!r)return e;var o=r.call(e),n,i=[],s;try{for(;(t===void 0||t-- >0)&&!(n=o.next()).done;)i.push(n.value)}catch(a){s={error:a}}finally{try{n&&!n.done&&(r=o.return)&&r.call(o)}finally{if(s)throw s.error}}return i}function B(e,t,r){if(r||arguments.length===2)for(var o=0,n=t.length,i;o1||c(d,S)})},v&&(n[d]=v(n[d])))}function c(d,v){try{p(o[d](v))}catch(S){u(i[0][3],S)}}function p(d){d.value instanceof dt?Promise.resolve(d.value.v).then(l,f):u(i[0][2],d)}function l(d){c("next",d)}function f(d){c("throw",d)}function u(d,v){d(v),i.shift(),i.length&&c(i[0][0],i[0][1])}}function To(e){if(!Symbol.asyncIterator)throw new TypeError("Symbol.asyncIterator is not defined.");var t=e[Symbol.asyncIterator],r;return t?t.call(e):(e=typeof Oe=="function"?Oe(e):e[Symbol.iterator](),r={},o("next"),o("throw"),o("return"),r[Symbol.asyncIterator]=function(){return this},r);function o(i){r[i]=e[i]&&function(s){return new Promise(function(a,c){s=e[i](s),n(a,c,s.done,s.value)})}}function n(i,s,a,c){Promise.resolve(c).then(function(p){i({value:p,done:a})},s)}}function I(e){return typeof e=="function"}function yt(e){var t=function(o){Error.call(o),o.stack=new Error().stack},r=e(t);return r.prototype=Object.create(Error.prototype),r.prototype.constructor=r,r}var Jt=yt(function(e){return function(r){e(this),this.message=r?r.length+` errors occurred during unsubscription: +`+r.map(function(o,n){return n+1+") "+o.toString()}).join(` + `):"",this.name="UnsubscriptionError",this.errors=r}});function Ze(e,t){if(e){var r=e.indexOf(t);0<=r&&e.splice(r,1)}}var qe=(function(){function e(t){this.initialTeardown=t,this.closed=!1,this._parentage=null,this._finalizers=null}return e.prototype.unsubscribe=function(){var t,r,o,n,i;if(!this.closed){this.closed=!0;var s=this._parentage;if(s)if(this._parentage=null,Array.isArray(s))try{for(var a=Oe(s),c=a.next();!c.done;c=a.next()){var p=c.value;p.remove(this)}}catch(S){t={error:S}}finally{try{c&&!c.done&&(r=a.return)&&r.call(a)}finally{if(t)throw t.error}}else s.remove(this);var l=this.initialTeardown;if(I(l))try{l()}catch(S){i=S instanceof Jt?S.errors:[S]}var f=this._finalizers;if(f){this._finalizers=null;try{for(var u=Oe(f),d=u.next();!d.done;d=u.next()){var v=d.value;try{So(v)}catch(S){i=i!=null?i:[],S instanceof Jt?i=B(B([],K(i)),K(S.errors)):i.push(S)}}}catch(S){o={error:S}}finally{try{d&&!d.done&&(n=u.return)&&n.call(u)}finally{if(o)throw o.error}}}if(i)throw new Jt(i)}},e.prototype.add=function(t){var r;if(t&&t!==this)if(this.closed)So(t);else{if(t instanceof e){if(t.closed||t._hasParent(this))return;t._addParent(this)}(this._finalizers=(r=this._finalizers)!==null&&r!==void 0?r:[]).push(t)}},e.prototype._hasParent=function(t){var r=this._parentage;return r===t||Array.isArray(r)&&r.includes(t)},e.prototype._addParent=function(t){var r=this._parentage;this._parentage=Array.isArray(r)?(r.push(t),r):r?[r,t]:t},e.prototype._removeParent=function(t){var r=this._parentage;r===t?this._parentage=null:Array.isArray(r)&&Ze(r,t)},e.prototype.remove=function(t){var r=this._finalizers;r&&Ze(r,t),t instanceof e&&t._removeParent(this)},e.EMPTY=(function(){var t=new e;return t.closed=!0,t})(),e})();var $r=qe.EMPTY;function Xt(e){return e instanceof qe||e&&"closed"in e&&I(e.remove)&&I(e.add)&&I(e.unsubscribe)}function So(e){I(e)?e():e.unsubscribe()}var De={onUnhandledError:null,onStoppedNotification:null,Promise:void 0,useDeprecatedSynchronousErrorHandling:!1,useDeprecatedNextContext:!1};var xt={setTimeout:function(e,t){for(var r=[],o=2;o0},enumerable:!1,configurable:!0}),t.prototype._trySubscribe=function(r){return this._throwIfClosed(),e.prototype._trySubscribe.call(this,r)},t.prototype._subscribe=function(r){return this._throwIfClosed(),this._checkFinalizedStatuses(r),this._innerSubscribe(r)},t.prototype._innerSubscribe=function(r){var o=this,n=this,i=n.hasError,s=n.isStopped,a=n.observers;return i||s?$r:(this.currentObservers=null,a.push(r),new qe(function(){o.currentObservers=null,Ze(a,r)}))},t.prototype._checkFinalizedStatuses=function(r){var o=this,n=o.hasError,i=o.thrownError,s=o.isStopped;n?r.error(i):s&&r.complete()},t.prototype.asObservable=function(){var r=new F;return r.source=this,r},t.create=function(r,o){return new Ho(r,o)},t})(F);var Ho=(function(e){ie(t,e);function t(r,o){var n=e.call(this)||this;return n.destination=r,n.source=o,n}return t.prototype.next=function(r){var o,n;(n=(o=this.destination)===null||o===void 0?void 0:o.next)===null||n===void 0||n.call(o,r)},t.prototype.error=function(r){var o,n;(n=(o=this.destination)===null||o===void 0?void 0:o.error)===null||n===void 0||n.call(o,r)},t.prototype.complete=function(){var r,o;(o=(r=this.destination)===null||r===void 0?void 0:r.complete)===null||o===void 0||o.call(r)},t.prototype._subscribe=function(r){var o,n;return(n=(o=this.source)===null||o===void 0?void 0:o.subscribe(r))!==null&&n!==void 0?n:$r},t})(T);var jr=(function(e){ie(t,e);function t(r){var o=e.call(this)||this;return o._value=r,o}return Object.defineProperty(t.prototype,"value",{get:function(){return this.getValue()},enumerable:!1,configurable:!0}),t.prototype._subscribe=function(r){var o=e.prototype._subscribe.call(this,r);return!o.closed&&r.next(this._value),o},t.prototype.getValue=function(){var r=this,o=r.hasError,n=r.thrownError,i=r._value;if(o)throw n;return this._throwIfClosed(),i},t.prototype.next=function(r){e.prototype.next.call(this,this._value=r)},t})(T);var Rt={now:function(){return(Rt.delegate||Date).now()},delegate:void 0};var It=(function(e){ie(t,e);function t(r,o,n){r===void 0&&(r=1/0),o===void 0&&(o=1/0),n===void 0&&(n=Rt);var i=e.call(this)||this;return i._bufferSize=r,i._windowTime=o,i._timestampProvider=n,i._buffer=[],i._infiniteTimeWindow=!0,i._infiniteTimeWindow=o===1/0,i._bufferSize=Math.max(1,r),i._windowTime=Math.max(1,o),i}return t.prototype.next=function(r){var o=this,n=o.isStopped,i=o._buffer,s=o._infiniteTimeWindow,a=o._timestampProvider,c=o._windowTime;n||(i.push(r),!s&&i.push(a.now()+c)),this._trimBuffer(),e.prototype.next.call(this,r)},t.prototype._subscribe=function(r){this._throwIfClosed(),this._trimBuffer();for(var o=this._innerSubscribe(r),n=this,i=n._infiniteTimeWindow,s=n._buffer,a=s.slice(),c=0;c0?e.prototype.schedule.call(this,r,o):(this.delay=o,this.state=r,this.scheduler.flush(this),this)},t.prototype.execute=function(r,o){return o>0||this.closed?e.prototype.execute.call(this,r,o):this._execute(r,o)},t.prototype.requestAsyncId=function(r,o,n){return n===void 0&&(n=0),n!=null&&n>0||n==null&&this.delay>0?e.prototype.requestAsyncId.call(this,r,o,n):(r.flush(this),0)},t})(St);var Ro=(function(e){ie(t,e);function t(){return e!==null&&e.apply(this,arguments)||this}return t})(Ot);var Dr=new Ro(Po);var Io=(function(e){ie(t,e);function t(r,o){var n=e.call(this,r,o)||this;return n.scheduler=r,n.work=o,n}return t.prototype.requestAsyncId=function(r,o,n){return n===void 0&&(n=0),n!==null&&n>0?e.prototype.requestAsyncId.call(this,r,o,n):(r.actions.push(this),r._scheduled||(r._scheduled=Tt.requestAnimationFrame(function(){return r.flush(void 0)})))},t.prototype.recycleAsyncId=function(r,o,n){var i;if(n===void 0&&(n=0),n!=null?n>0:this.delay>0)return e.prototype.recycleAsyncId.call(this,r,o,n);var s=r.actions;o!=null&&o===r._scheduled&&((i=s[s.length-1])===null||i===void 0?void 0:i.id)!==o&&(Tt.cancelAnimationFrame(o),r._scheduled=void 0)},t})(St);var Fo=(function(e){ie(t,e);function t(){return e!==null&&e.apply(this,arguments)||this}return t.prototype.flush=function(r){this._active=!0;var o;r?o=r.id:(o=this._scheduled,this._scheduled=void 0);var n=this.actions,i;r=r||n.shift();do if(i=r.execute(r.state,r.delay))break;while((r=n[0])&&r.id===o&&n.shift());if(this._active=!1,i){for(;(r=n[0])&&r.id===o&&n.shift();)r.unsubscribe();throw i}},t})(Ot);var ye=new Fo(Io);var y=new F(function(e){return e.complete()});function tr(e){return e&&I(e.schedule)}function Vr(e){return e[e.length-1]}function pt(e){return I(Vr(e))?e.pop():void 0}function Fe(e){return tr(Vr(e))?e.pop():void 0}function rr(e,t){return typeof Vr(e)=="number"?e.pop():t}var Lt=(function(e){return e&&typeof e.length=="number"&&typeof e!="function"});function or(e){return I(e==null?void 0:e.then)}function nr(e){return I(e[wt])}function ir(e){return Symbol.asyncIterator&&I(e==null?void 0:e[Symbol.asyncIterator])}function ar(e){return new TypeError("You provided "+(e!==null&&typeof e=="object"?"an invalid object":"'"+e+"'")+" where a stream was expected. You can provide an Observable, Promise, ReadableStream, Array, AsyncIterable, or Iterable.")}function fa(){return typeof Symbol!="function"||!Symbol.iterator?"@@iterator":Symbol.iterator}var sr=fa();function cr(e){return I(e==null?void 0:e[sr])}function pr(e){return wo(this,arguments,function(){var r,o,n,i;return Gt(this,function(s){switch(s.label){case 0:r=e.getReader(),s.label=1;case 1:s.trys.push([1,,9,10]),s.label=2;case 2:return[4,dt(r.read())];case 3:return o=s.sent(),n=o.value,i=o.done,i?[4,dt(void 0)]:[3,5];case 4:return[2,s.sent()];case 5:return[4,dt(n)];case 6:return[4,s.sent()];case 7:return s.sent(),[3,2];case 8:return[3,10];case 9:return r.releaseLock(),[7];case 10:return[2]}})})}function lr(e){return I(e==null?void 0:e.getReader)}function U(e){if(e instanceof F)return e;if(e!=null){if(nr(e))return ua(e);if(Lt(e))return da(e);if(or(e))return ha(e);if(ir(e))return jo(e);if(cr(e))return ba(e);if(lr(e))return va(e)}throw ar(e)}function ua(e){return new F(function(t){var r=e[wt]();if(I(r.subscribe))return r.subscribe(t);throw new TypeError("Provided object does not correctly implement Symbol.observable")})}function da(e){return new F(function(t){for(var r=0;r=2;return function(o){return o.pipe(e?g(function(n,i){return e(n,i,o)}):be,Ee(1),r?Qe(t):tn(function(){return new fr}))}}function Yr(e){return e<=0?function(){return y}:E(function(t,r){var o=[];t.subscribe(w(r,function(n){o.push(n),e=2,!0))}function le(e){e===void 0&&(e={});var t=e.connector,r=t===void 0?function(){return new T}:t,o=e.resetOnError,n=o===void 0?!0:o,i=e.resetOnComplete,s=i===void 0?!0:i,a=e.resetOnRefCountZero,c=a===void 0?!0:a;return function(p){var l,f,u,d=0,v=!1,S=!1,X=function(){f==null||f.unsubscribe(),f=void 0},re=function(){X(),l=u=void 0,v=S=!1},ee=function(){var k=l;re(),k==null||k.unsubscribe()};return E(function(k,ut){d++,!S&&!v&&X();var je=u=u!=null?u:r();ut.add(function(){d--,d===0&&!S&&!v&&(f=Br(ee,c))}),je.subscribe(ut),!l&&d>0&&(l=new bt({next:function(R){return je.next(R)},error:function(R){S=!0,X(),f=Br(re,n,R),je.error(R)},complete:function(){v=!0,X(),f=Br(re,s),je.complete()}}),U(k).subscribe(l))})(p)}}function Br(e,t){for(var r=[],o=2;oe.next(document)),e}function M(e,t=document){return Array.from(t.querySelectorAll(e))}function j(e,t=document){let r=ue(e,t);if(typeof r=="undefined")throw new ReferenceError(`Missing element: expected "${e}" to be present`);return r}function ue(e,t=document){return t.querySelector(e)||void 0}function Ne(){var e,t,r,o;return(o=(r=(t=(e=document.activeElement)==null?void 0:e.shadowRoot)==null?void 0:t.activeElement)!=null?r:document.activeElement)!=null?o:void 0}var Ra=L(h(document.body,"focusin"),h(document.body,"focusout")).pipe(Ae(1),Q(void 0),m(()=>Ne()||document.body),Z(1));function Ye(e){return Ra.pipe(m(t=>e.contains(t)),Y())}function it(e,t){return H(()=>L(h(e,"mouseenter").pipe(m(()=>!0)),h(e,"mouseleave").pipe(m(()=>!1))).pipe(t?jt(r=>He(+!r*t)):be,Q(e.matches(":hover"))))}function sn(e,t){if(typeof t=="string"||typeof t=="number")e.innerHTML+=t.toString();else if(t instanceof Node)e.appendChild(t);else if(Array.isArray(t))for(let r of t)sn(e,r)}function x(e,t,...r){let o=document.createElement(e);if(t)for(let n of Object.keys(t))typeof t[n]!="undefined"&&(typeof t[n]!="boolean"?o.setAttribute(n,t[n]):o.setAttribute(n,""));for(let n of r)sn(o,n);return o}function br(e){if(e>999){let t=+((e-950)%1e3>99);return`${((e+1e-6)/1e3).toFixed(t)}k`}else return e.toString()}function _t(e){let t=x("script",{src:e});return H(()=>(document.head.appendChild(t),L(h(t,"load"),h(t,"error").pipe(b(()=>Nr(()=>new ReferenceError(`Invalid script: ${e}`))))).pipe(m(()=>{}),A(()=>document.head.removeChild(t)),Ee(1))))}var cn=new T,Ia=H(()=>typeof ResizeObserver=="undefined"?_t("https://unpkg.com/resize-observer-polyfill"):$(void 0)).pipe(m(()=>new ResizeObserver(e=>e.forEach(t=>cn.next(t)))),b(e=>L(tt,$(e)).pipe(A(()=>e.disconnect()))),Z(1));function de(e){return{width:e.offsetWidth,height:e.offsetHeight}}function Le(e){let t=e;for(;t.clientWidth===0&&t.parentElement;)t=t.parentElement;return Ia.pipe(O(r=>r.observe(t)),b(r=>cn.pipe(g(o=>o.target===t),A(()=>r.unobserve(t)))),m(()=>de(e)),Q(de(e)))}function At(e){return{width:e.scrollWidth,height:e.scrollHeight}}function vr(e){let t=e.parentElement;for(;t&&(e.scrollWidth<=t.scrollWidth&&e.scrollHeight<=t.scrollHeight);)t=(e=t).parentElement;return t?e:void 0}function pn(e){let t=[],r=e.parentElement;for(;r;)(e.clientWidth>r.clientWidth||e.clientHeight>r.clientHeight)&&t.push(r),r=(e=r).parentElement;return t.length===0&&t.push(document.documentElement),t}function Be(e){return{x:e.offsetLeft,y:e.offsetTop}}function ln(e){let t=e.getBoundingClientRect();return{x:t.x+window.scrollX,y:t.y+window.scrollY}}function mn(e){return L(h(window,"load"),h(window,"resize")).pipe($e(0,ye),m(()=>Be(e)),Q(Be(e)))}function gr(e){return{x:e.scrollLeft,y:e.scrollTop}}function Ge(e){return L(h(e,"scroll"),h(window,"scroll"),h(window,"resize")).pipe($e(0,ye),m(()=>gr(e)),Q(gr(e)))}var fn=new T,Fa=H(()=>$(new IntersectionObserver(e=>{for(let t of e)fn.next(t)},{threshold:0}))).pipe(b(e=>L(tt,$(e)).pipe(A(()=>e.disconnect()))),Z(1));function mt(e){return Fa.pipe(O(t=>t.observe(e)),b(t=>fn.pipe(g(({target:r})=>r===e),A(()=>t.unobserve(e)),m(({isIntersecting:r})=>r))))}function un(e,t=16){return Ge(e).pipe(m(({y:r})=>{let o=de(e),n=At(e);return r>=n.height-o.height-t}),Y())}var yr={drawer:j("[data-md-toggle=drawer]"),search:j("[data-md-toggle=search]")};function dn(e){return yr[e].checked}function at(e,t){yr[e].checked!==t&&yr[e].click()}function Je(e){let t=yr[e];return h(t,"change").pipe(m(()=>t.checked),Q(t.checked))}function ja(e,t){switch(e.constructor){case HTMLInputElement:return e.type==="radio"?/^Arrow/.test(t):!0;case HTMLSelectElement:case HTMLTextAreaElement:return!0;default:return e.isContentEditable}}function Ua(){return L(h(window,"compositionstart").pipe(m(()=>!0)),h(window,"compositionend").pipe(m(()=>!1))).pipe(Q(!1))}function hn(){let e=h(window,"keydown").pipe(g(t=>!(t.metaKey||t.ctrlKey)),m(t=>({mode:dn("search")?"search":"global",type:t.key,claim(){t.preventDefault(),t.stopPropagation()}})),g(({mode:t,type:r})=>{if(t==="global"){let o=Ne();if(typeof o!="undefined")return!ja(o,r)}return!0}),le());return Ua().pipe(b(t=>t?y:e))}function we(){return new URL(location.href)}function st(e,t=!1){if(V("navigation.instant")&&!t){let r=x("a",{href:e.href});document.body.appendChild(r),r.click(),r.remove()}else location.href=e.href}function bn(){return new T}function vn(){return location.hash.slice(1)}function gn(e){let t=x("a",{href:e});t.addEventListener("click",r=>r.stopPropagation()),t.click()}function Zr(e){return L(h(window,"hashchange"),e).pipe(m(vn),Q(vn()),g(t=>t.length>0),Z(1))}function yn(e){return Zr(e).pipe(m(t=>ue(`[id="${t}"]`)),g(t=>typeof t!="undefined"))}function Wt(e){let t=matchMedia(e);return ur(r=>t.addListener(()=>r(t.matches))).pipe(Q(t.matches))}function xn(){let e=matchMedia("print");return L(h(window,"beforeprint").pipe(m(()=>!0)),h(window,"afterprint").pipe(m(()=>!1))).pipe(Q(e.matches))}function eo(e,t){return e.pipe(b(r=>r?t():y))}function to(e,t){return new F(r=>{let o=new XMLHttpRequest;return o.open("GET",`${e}`),o.responseType="blob",o.addEventListener("load",()=>{o.status>=200&&o.status<300?(r.next(o.response),r.complete()):r.error(new Error(o.statusText))}),o.addEventListener("error",()=>{r.error(new Error("Network error"))}),o.addEventListener("abort",()=>{r.complete()}),typeof(t==null?void 0:t.progress$)!="undefined"&&(o.addEventListener("progress",n=>{var i;if(n.lengthComputable)t.progress$.next(n.loaded/n.total*100);else{let s=(i=o.getResponseHeader("Content-Length"))!=null?i:0;t.progress$.next(n.loaded/+s*100)}}),t.progress$.next(5)),o.send(),()=>o.abort()})}function ze(e,t){return to(e,t).pipe(b(r=>r.text()),m(r=>JSON.parse(r)),Z(1))}function xr(e,t){let r=new DOMParser;return to(e,t).pipe(b(o=>o.text()),m(o=>r.parseFromString(o,"text/html")),Z(1))}function En(e,t){let r=new DOMParser;return to(e,t).pipe(b(o=>o.text()),m(o=>r.parseFromString(o,"text/xml")),Z(1))}function wn(){return{x:Math.max(0,scrollX),y:Math.max(0,scrollY)}}function Tn(){return L(h(window,"scroll",{passive:!0}),h(window,"resize",{passive:!0})).pipe(m(wn),Q(wn()))}function Sn(){return{width:innerWidth,height:innerHeight}}function On(){return h(window,"resize",{passive:!0}).pipe(m(Sn),Q(Sn()))}function Ln(){return z([Tn(),On()]).pipe(m(([e,t])=>({offset:e,size:t})),Z(1))}function Er(e,{viewport$:t,header$:r}){let o=t.pipe(ne("size")),n=z([o,r]).pipe(m(()=>Be(e)));return z([r,t,n]).pipe(m(([{height:i},{offset:s,size:a},{x:c,y:p}])=>({offset:{x:s.x-c,y:s.y-p+i},size:a})))}function Wa(e){return h(e,"message",t=>t.data)}function Da(e){let t=new T;return t.subscribe(r=>e.postMessage(r)),t}function Mn(e,t=new Worker(e)){let r=Wa(t),o=Da(t),n=new T;n.subscribe(o);let i=o.pipe(oe(),ae(!0));return n.pipe(oe(),Ve(r.pipe(W(i))),le())}var Va=j("#__config"),Ct=JSON.parse(Va.textContent);Ct.base=`${new URL(Ct.base,we())}`;function Te(){return Ct}function V(e){return Ct.features.includes(e)}function Me(e,t){return typeof t!="undefined"?Ct.translations[e].replace("#",t.toString()):Ct.translations[e]}function Ce(e,t=document){return j(`[data-md-component=${e}]`,t)}function me(e,t=document){return M(`[data-md-component=${e}]`,t)}function Na(e){let t=j(".md-typeset > :first-child",e);return h(t,"click",{once:!0}).pipe(m(()=>j(".md-typeset",e)),m(r=>({hash:__md_hash(r.innerHTML)})))}function _n(e){if(!V("announce.dismiss")||!e.childElementCount)return y;if(!e.hidden){let t=j(".md-typeset",e);__md_hash(t.innerHTML)===__md_get("__announce")&&(e.hidden=!0)}return H(()=>{let t=new T;return t.subscribe(({hash:r})=>{e.hidden=!0,__md_set("__announce",r)}),Na(e).pipe(O(r=>t.next(r)),A(()=>t.complete()),m(r=>P({ref:e},r)))})}function za(e,{target$:t}){return t.pipe(m(r=>({hidden:r!==e})))}function An(e,t){let r=new T;return r.subscribe(({hidden:o})=>{e.hidden=o}),za(e,t).pipe(O(o=>r.next(o)),A(()=>r.complete()),m(o=>P({ref:e},o)))}function Dt(e,t){return t==="inline"?x("div",{class:"md-tooltip md-tooltip--inline",id:e,role:"tooltip"},x("div",{class:"md-tooltip__inner md-typeset"})):x("div",{class:"md-tooltip",id:e,role:"tooltip"},x("div",{class:"md-tooltip__inner md-typeset"}))}function wr(...e){return x("div",{class:"md-tooltip2",role:"dialog"},x("div",{class:"md-tooltip2__inner md-typeset"},e))}function Cn(...e){return x("div",{class:"md-tooltip2",role:"tooltip"},x("div",{class:"md-tooltip2__inner md-typeset"},e))}function kn(e,t){if(t=t?`${t}_annotation_${e}`:void 0,t){let r=t?`#${t}`:void 0;return x("aside",{class:"md-annotation",tabIndex:0},Dt(t),x("a",{href:r,class:"md-annotation__index",tabIndex:-1},x("span",{"data-md-annotation-id":e})))}else return x("aside",{class:"md-annotation",tabIndex:0},Dt(t),x("span",{class:"md-annotation__index",tabIndex:-1},x("span",{"data-md-annotation-id":e})))}function Hn(e){return x("button",{class:"md-code__button",title:Me("clipboard.copy"),"data-clipboard-target":`#${e} > code`,"data-md-type":"copy"})}function $n(){return x("button",{class:"md-code__button",title:"Toggle line selection","data-md-type":"select"})}function Pn(){return x("nav",{class:"md-code__nav"})}var In=$t(ro());function oo(e,t){let r=t&2,o=t&1,n=Object.keys(e.terms).filter(c=>!e.terms[c]).reduce((c,p)=>[...c,x("del",null,(0,In.default)(p))," "],[]).slice(0,-1),i=Te(),s=new URL(e.location,i.base);V("search.highlight")&&s.searchParams.set("h",Object.entries(e.terms).filter(([,c])=>c).reduce((c,[p])=>`${c} ${p}`.trim(),""));let{tags:a}=Te();return x("a",{href:`${s}`,class:"md-search-result__link",tabIndex:-1},x("article",{class:"md-search-result__article md-typeset","data-md-score":e.score.toFixed(2)},r>0&&x("div",{class:"md-search-result__icon md-icon"}),r>0&&x("h1",null,e.title),r<=0&&x("h2",null,e.title),o>0&&e.text.length>0&&e.text,e.tags&&x("nav",{class:"md-tags"},e.tags.map(c=>{let p=a?c in a?`md-tag-icon md-tag--${a[c]}`:"md-tag-icon":"";return x("span",{class:`md-tag ${p}`},c)})),o>0&&n.length>0&&x("p",{class:"md-search-result__terms"},Me("search.result.term.missing"),": ",...n)))}function Fn(e){let t=e[0].score,r=[...e],o=Te(),n=r.findIndex(l=>!`${new URL(l.location,o.base)}`.includes("#")),[i]=r.splice(n,1),s=r.findIndex(l=>l.scoreoo(l,1)),...c.length?[x("details",{class:"md-search-result__more"},x("summary",{tabIndex:-1},x("div",null,c.length>0&&c.length===1?Me("search.result.more.one"):Me("search.result.more.other",c.length))),...c.map(l=>oo(l,1)))]:[]];return x("li",{class:"md-search-result__item"},p)}function jn(e){return x("ul",{class:"md-source__facts"},Object.entries(e).map(([t,r])=>x("li",{class:`md-source__fact md-source__fact--${t}`},typeof r=="number"?br(r):r)))}function no(e){let t=`tabbed-control tabbed-control--${e}`;return x("div",{class:t,hidden:!0},x("button",{class:"tabbed-button",tabIndex:-1,"aria-hidden":"true"}))}function Un(e){return x("div",{class:"md-typeset__scrollwrap"},x("div",{class:"md-typeset__table"},e))}function Qa(e){var o;let t=Te(),r=new URL(`../${e.version}/`,t.base);return x("li",{class:"md-version__item"},x("a",{href:`${r}`,class:"md-version__link"},e.title,((o=t.version)==null?void 0:o.alias)&&e.aliases.length>0&&x("span",{class:"md-version__alias"},e.aliases[0])))}function Wn(e,t){var o;let r=Te();return e=e.filter(n=>{var i;return!((i=n.properties)!=null&&i.hidden)}),x("div",{class:"md-version"},x("button",{class:"md-version__current","aria-label":Me("select.version")},t.title,((o=r.version)==null?void 0:o.alias)&&t.aliases.length>0&&x("span",{class:"md-version__alias"},t.aliases[0])),x("ul",{class:"md-version__list"},e.map(Qa)))}var Ya=0;function Ba(e,t=250){let r=z([Ye(e),it(e,t)]).pipe(m(([n,i])=>n||i),Y()),o=H(()=>pn(e)).pipe(J(Ge),gt(1),Pe(r),m(()=>ln(e)));return r.pipe(Re(n=>n),b(()=>z([r,o])),m(([n,i])=>({active:n,offset:i})),le())}function Vt(e,t,r=250){let{content$:o,viewport$:n}=t,i=`__tooltip2_${Ya++}`;return H(()=>{let s=new T,a=new jr(!1);s.pipe(oe(),ae(!1)).subscribe(a);let c=a.pipe(jt(l=>He(+!l*250,Dr)),Y(),b(l=>l?o:y),O(l=>l.id=i),le());z([s.pipe(m(({active:l})=>l)),c.pipe(b(l=>it(l,250)),Q(!1))]).pipe(m(l=>l.some(f=>f))).subscribe(a);let p=a.pipe(g(l=>l),te(c,n),m(([l,f,{size:u}])=>{let d=e.getBoundingClientRect(),v=d.width/2;if(f.role==="tooltip")return{x:v,y:8+d.height};if(d.y>=u.height/2){let{height:S}=de(f);return{x:v,y:-16-S}}else return{x:v,y:16+d.height}}));return z([c,s,p]).subscribe(([l,{offset:f},u])=>{l.style.setProperty("--md-tooltip-host-x",`${f.x}px`),l.style.setProperty("--md-tooltip-host-y",`${f.y}px`),l.style.setProperty("--md-tooltip-x",`${u.x}px`),l.style.setProperty("--md-tooltip-y",`${u.y}px`),l.classList.toggle("md-tooltip2--top",u.y<0),l.classList.toggle("md-tooltip2--bottom",u.y>=0)}),a.pipe(g(l=>l),te(c,(l,f)=>f),g(l=>l.role==="tooltip")).subscribe(l=>{let f=de(j(":scope > *",l));l.style.setProperty("--md-tooltip-width",`${f.width}px`),l.style.setProperty("--md-tooltip-tail","0px")}),a.pipe(Y(),xe(ye),te(c)).subscribe(([l,f])=>{f.classList.toggle("md-tooltip2--active",l)}),z([a.pipe(g(l=>l)),c]).subscribe(([l,f])=>{f.role==="dialog"?(e.setAttribute("aria-controls",i),e.setAttribute("aria-haspopup","dialog")):e.setAttribute("aria-describedby",i)}),a.pipe(g(l=>!l)).subscribe(()=>{e.removeAttribute("aria-controls"),e.removeAttribute("aria-describedby"),e.removeAttribute("aria-haspopup")}),Ba(e,r).pipe(O(l=>s.next(l)),A(()=>s.complete()),m(l=>P({ref:e},l)))})}function Xe(e,{viewport$:t},r=document.body){return Vt(e,{content$:new F(o=>{let n=e.title,i=Cn(n);return o.next(i),e.removeAttribute("title"),r.append(i),()=>{i.remove(),e.setAttribute("title",n)}}),viewport$:t},0)}function Ga(e,t){let r=H(()=>z([mn(e),Ge(t)])).pipe(m(([{x:o,y:n},i])=>{let{width:s,height:a}=de(e);return{x:o-i.x+s/2,y:n-i.y+a/2}}));return Ye(e).pipe(b(o=>r.pipe(m(n=>({active:o,offset:n})),Ee(+!o||1/0))))}function Dn(e,t,{target$:r}){let[o,n]=Array.from(e.children);return H(()=>{let i=new T,s=i.pipe(oe(),ae(!0));return i.subscribe({next({offset:a}){e.style.setProperty("--md-tooltip-x",`${a.x}px`),e.style.setProperty("--md-tooltip-y",`${a.y}px`)},complete(){e.style.removeProperty("--md-tooltip-x"),e.style.removeProperty("--md-tooltip-y")}}),mt(e).pipe(W(s)).subscribe(a=>{e.toggleAttribute("data-md-visible",a)}),L(i.pipe(g(({active:a})=>a)),i.pipe(Ae(250),g(({active:a})=>!a))).subscribe({next({active:a}){a?e.prepend(o):o.remove()},complete(){e.prepend(o)}}),i.pipe($e(16,ye)).subscribe(({active:a})=>{o.classList.toggle("md-tooltip--active",a)}),i.pipe(gt(125,ye),g(()=>!!e.offsetParent),m(()=>e.offsetParent.getBoundingClientRect()),m(({x:a})=>a)).subscribe({next(a){a?e.style.setProperty("--md-tooltip-0",`${-a}px`):e.style.removeProperty("--md-tooltip-0")},complete(){e.style.removeProperty("--md-tooltip-0")}}),h(n,"click").pipe(W(s),g(a=>!(a.metaKey||a.ctrlKey))).subscribe(a=>{a.stopPropagation(),a.preventDefault()}),h(n,"mousedown").pipe(W(s),te(i)).subscribe(([a,{active:c}])=>{var p;if(a.button!==0||a.metaKey||a.ctrlKey)a.preventDefault();else if(c){a.preventDefault();let l=e.parentElement.closest(".md-annotation");l instanceof HTMLElement?l.focus():(p=Ne())==null||p.blur()}}),r.pipe(W(s),g(a=>a===o),nt(125)).subscribe(()=>e.focus()),Ga(e,t).pipe(O(a=>i.next(a)),A(()=>i.complete()),m(a=>P({ref:e},a)))})}function Ja(e){let t=Te();if(e.tagName!=="CODE")return[e];let r=[".c",".c1",".cm"];if(t.annotate&&typeof t.annotate=="object"){let o=e.closest("[class|=language]");if(o)for(let n of Array.from(o.classList)){if(!n.startsWith("language-"))continue;let[,i]=n.split("-");i in t.annotate&&r.push(...t.annotate[i])}}return M(r.join(", "),e)}function Xa(e){let t=[];for(let r of Ja(e)){let o=[],n=document.createNodeIterator(r,NodeFilter.SHOW_TEXT);for(let i=n.nextNode();i;i=n.nextNode())o.push(i);for(let i of o){let s;for(;s=/(\(\d+\))(!)?/.exec(i.textContent);){let[,a,c]=s;if(typeof c=="undefined"){let p=i.splitText(s.index);i=p.splitText(a.length),t.push(p)}else{i.textContent=a,t.push(i);break}}}}return t}function Vn(e,t){t.append(...Array.from(e.childNodes))}function Tr(e,t,{target$:r,print$:o}){let n=t.closest("[id]"),i=n==null?void 0:n.id,s=new Map;for(let a of Xa(t)){let[,c]=a.textContent.match(/\((\d+)\)/);ue(`:scope > li:nth-child(${c})`,e)&&(s.set(c,kn(c,i)),a.replaceWith(s.get(c)))}return s.size===0?y:H(()=>{let a=new T,c=a.pipe(oe(),ae(!0)),p=[];for(let[l,f]of s)p.push([j(".md-typeset",f),j(`:scope > li:nth-child(${l})`,e)]);return o.pipe(W(c)).subscribe(l=>{e.hidden=!l,e.classList.toggle("md-annotation-list",l);for(let[f,u]of p)l?Vn(f,u):Vn(u,f)}),L(...[...s].map(([,l])=>Dn(l,t,{target$:r}))).pipe(A(()=>a.complete()),le())})}function Nn(e){if(e.nextElementSibling){let t=e.nextElementSibling;if(t.tagName==="OL")return t;if(t.tagName==="P"&&!t.children.length)return Nn(t)}}function zn(e,t){return H(()=>{let r=Nn(e);return typeof r!="undefined"?Tr(r,e,t):y})}var Kn=$t(ao());var Za=0,qn=L(h(window,"keydown").pipe(m(()=>!0)),L(h(window,"keyup"),h(window,"contextmenu")).pipe(m(()=>!1))).pipe(Q(!1),Z(1));function Qn(e){if(e.nextElementSibling){let t=e.nextElementSibling;if(t.tagName==="OL")return t;if(t.tagName==="P"&&!t.children.length)return Qn(t)}}function es(e){return Le(e).pipe(m(({width:t})=>({scrollable:At(e).width>t})),ne("scrollable"))}function Yn(e,t){let{matches:r}=matchMedia("(hover)"),o=H(()=>{let n=new T,i=n.pipe(Yr(1));n.subscribe(({scrollable:d})=>{d&&r?e.setAttribute("tabindex","0"):e.removeAttribute("tabindex")});let s=[],a=e.closest("pre"),c=a.closest("[id]"),p=c?c.id:Za++;a.id=`__code_${p}`;let l=[],f=e.closest(".highlight");if(f instanceof HTMLElement){let d=Qn(f);if(typeof d!="undefined"&&(f.classList.contains("annotate")||V("content.code.annotate"))){let v=Tr(d,e,t);l.push(Le(f).pipe(W(i),m(({width:S,height:X})=>S&&X),Y(),b(S=>S?v:y)))}}let u=M(":scope > span[id]",e);if(u.length&&(e.classList.add("md-code__content"),e.closest(".select")||V("content.code.select")&&!e.closest(".no-select"))){let d=+u[0].id.split("-").pop(),v=$n();s.push(v),V("content.tooltips")&&l.push(Xe(v,{viewport$}));let S=h(v,"click").pipe(Ut(R=>!R,!1),O(()=>v.blur()),le());S.subscribe(R=>{v.classList.toggle("md-code__button--active",R)});let X=fe(u).pipe(J(R=>it(R).pipe(m(se=>[R,se]))));S.pipe(b(R=>R?X:y)).subscribe(([R,se])=>{let ce=ue(".hll.select",R);if(ce&&!se)ce.replaceWith(...Array.from(ce.childNodes));else if(!ce&&se){let he=document.createElement("span");he.className="hll select",he.append(...Array.from(R.childNodes).slice(1)),R.append(he)}});let re=fe(u).pipe(J(R=>h(R,"mousedown").pipe(O(se=>se.preventDefault()),m(()=>R)))),ee=S.pipe(b(R=>R?re:y),te(qn),m(([R,se])=>{var he;let ce=u.indexOf(R)+d;if(se===!1)return[ce,ce];{let Se=M(".hll",e).map(Ue=>u.indexOf(Ue.parentElement)+d);return(he=window.getSelection())==null||he.removeAllRanges(),[Math.min(ce,...Se),Math.max(ce,...Se)]}})),k=Zr(y).pipe(g(R=>R.startsWith(`__codelineno-${p}-`)));k.subscribe(R=>{let[,,se]=R.split("-"),ce=se.split(":").map(Se=>+Se-d+1);ce.length===1&&ce.push(ce[0]);for(let Se of M(".hll:not(.select)",e))Se.replaceWith(...Array.from(Se.childNodes));let he=u.slice(ce[0]-1,ce[1]);for(let Se of he){let Ue=document.createElement("span");Ue.className="hll",Ue.append(...Array.from(Se.childNodes).slice(1)),Se.append(Ue)}}),k.pipe(Ee(1),xe(pe)).subscribe(R=>{if(R.includes(":")){let se=document.getElementById(R.split(":")[0]);se&&setTimeout(()=>{let ce=se,he=-64;for(;ce!==document.body;)he+=ce.offsetTop,ce=ce.offsetParent;window.scrollTo({top:he})},1)}});let je=fe(M('a[href^="#__codelineno"]',f)).pipe(J(R=>h(R,"click").pipe(O(se=>se.preventDefault()),m(()=>R)))).pipe(W(i),te(qn),m(([R,se])=>{let he=+j(`[id="${R.hash.slice(1)}"]`).parentElement.id.split("-").pop();if(se===!1)return[he,he];{let Se=M(".hll",e).map(Ue=>+Ue.parentElement.id.split("-").pop());return[Math.min(he,...Se),Math.max(he,...Se)]}}));L(ee,je).subscribe(R=>{let se=`#__codelineno-${p}-`;R[0]===R[1]?se+=R[0]:se+=`${R[0]}:${R[1]}`,history.replaceState({},"",se),window.dispatchEvent(new HashChangeEvent("hashchange",{newURL:window.location.origin+window.location.pathname+se,oldURL:window.location.href}))})}if(Kn.default.isSupported()&&(e.closest(".copy")||V("content.code.copy")&&!e.closest(".no-copy"))){let d=Hn(a.id);s.push(d),V("content.tooltips")&&l.push(Xe(d,{viewport$}))}if(s.length){let d=Pn();d.append(...s),a.insertBefore(d,e)}return es(e).pipe(O(d=>n.next(d)),A(()=>n.complete()),m(d=>P({ref:e},d)),Ve(L(...l).pipe(W(i))))});return V("content.lazy")?mt(e).pipe(g(n=>n),Ee(1),b(()=>o)):o}function ts(e,{target$:t,print$:r}){let o=!0;return L(t.pipe(m(n=>n.closest("details:not([open])")),g(n=>e===n),m(()=>({action:"open",reveal:!0}))),r.pipe(g(n=>n||!o),O(()=>o=e.open),m(n=>({action:n?"open":"close"}))))}function Bn(e,t){return H(()=>{let r=new T;return r.subscribe(({action:o,reveal:n})=>{e.toggleAttribute("open",o==="open"),n&&e.scrollIntoView()}),ts(e,t).pipe(O(o=>r.next(o)),A(()=>r.complete()),m(o=>P({ref:e},o)))})}var Gn=0;function rs(e){let t=document.createElement("h3");t.innerHTML=e.innerHTML;let r=[t],o=e.nextElementSibling;for(;o&&!(o instanceof HTMLHeadingElement);)r.push(o),o=o.nextElementSibling;return r}function os(e,t){for(let r of M("[href], [src]",e))for(let o of["href","src"]){let n=r.getAttribute(o);if(n&&!/^(?:[a-z]+:)?\/\//i.test(n)){r[o]=new URL(r.getAttribute(o),t).toString();break}}for(let r of M("[name^=__], [for]",e))for(let o of["id","for","name"]){let n=r.getAttribute(o);n&&r.setAttribute(o,`${n}$preview_${Gn}`)}return Gn++,$(e)}function Jn(e,t){let{sitemap$:r}=t;if(!(e instanceof HTMLAnchorElement))return y;if(!(V("navigation.instant.preview")||e.hasAttribute("data-preview")))return y;e.removeAttribute("title");let o=z([Ye(e),it(e)]).pipe(m(([i,s])=>i||s),Y(),g(i=>i));return rt([r,o]).pipe(b(([i])=>{let s=new URL(e.href);return s.search=s.hash="",i.has(`${s}`)?$(s):y}),b(i=>xr(i).pipe(b(s=>os(s,i)))),b(i=>{let s=e.hash?`article [id="${e.hash.slice(1)}"]`:"article h1",a=ue(s,i);return typeof a=="undefined"?y:$(rs(a))})).pipe(b(i=>{let s=new F(a=>{let c=wr(...i);return a.next(c),document.body.append(c),()=>c.remove()});return Vt(e,P({content$:s},t))}))}var Xn=".node circle,.node ellipse,.node path,.node polygon,.node rect{fill:var(--md-mermaid-node-bg-color);stroke:var(--md-mermaid-node-fg-color)}marker{fill:var(--md-mermaid-edge-color)!important}.edgeLabel .label rect{fill:#0000}.flowchartTitleText{fill:var(--md-mermaid-label-fg-color)}.label{color:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.label foreignObject{line-height:normal;overflow:visible}.label div .edgeLabel{color:var(--md-mermaid-label-fg-color)}.edgeLabel,.edgeLabel p,.label div .edgeLabel{background-color:var(--md-mermaid-label-bg-color)}.edgeLabel,.edgeLabel p{fill:var(--md-mermaid-label-bg-color);color:var(--md-mermaid-edge-color)}.edgePath .path,.flowchart-link{stroke:var(--md-mermaid-edge-color)}.edgePath .arrowheadPath{fill:var(--md-mermaid-edge-color);stroke:none}.cluster rect{fill:var(--md-default-fg-color--lightest);stroke:var(--md-default-fg-color--lighter)}.cluster span{color:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}g #flowchart-circleEnd,g #flowchart-circleStart,g #flowchart-crossEnd,g #flowchart-crossStart,g #flowchart-pointEnd,g #flowchart-pointStart{stroke:none}.classDiagramTitleText{fill:var(--md-mermaid-label-fg-color)}g.classGroup line,g.classGroup rect{fill:var(--md-mermaid-node-bg-color);stroke:var(--md-mermaid-node-fg-color)}g.classGroup text{fill:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.classLabel .box{fill:var(--md-mermaid-label-bg-color);background-color:var(--md-mermaid-label-bg-color);opacity:1}.classLabel .label{fill:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.node .divider{stroke:var(--md-mermaid-node-fg-color)}.relation{stroke:var(--md-mermaid-edge-color)}.cardinality{fill:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}.cardinality text{fill:inherit!important}defs marker.marker.composition.class path,defs marker.marker.dependency.class path,defs marker.marker.extension.class path{fill:var(--md-mermaid-edge-color)!important;stroke:var(--md-mermaid-edge-color)!important}defs marker.marker.aggregation.class path{fill:var(--md-mermaid-label-bg-color)!important;stroke:var(--md-mermaid-edge-color)!important}.statediagramTitleText{fill:var(--md-mermaid-label-fg-color)}g.stateGroup rect{fill:var(--md-mermaid-node-bg-color);stroke:var(--md-mermaid-node-fg-color)}g.stateGroup .state-title{fill:var(--md-mermaid-label-fg-color)!important;font-family:var(--md-mermaid-font-family)}g.stateGroup .composit{fill:var(--md-mermaid-label-bg-color)}.nodeLabel,.nodeLabel p{color:var(--md-mermaid-label-fg-color);font-family:var(--md-mermaid-font-family)}a .nodeLabel{text-decoration:underline}.node circle.state-end,.node circle.state-start,.start-state{fill:var(--md-mermaid-edge-color);stroke:none}.end-state-inner,.end-state-outer{fill:var(--md-mermaid-edge-color)}.end-state-inner,.node circle.state-end{stroke:var(--md-mermaid-label-bg-color)}.transition{stroke:var(--md-mermaid-edge-color)}[id^=state-fork] rect,[id^=state-join] rect{fill:var(--md-mermaid-edge-color)!important;stroke:none!important}.statediagram-cluster.statediagram-cluster .inner{fill:var(--md-default-bg-color)}.statediagram-cluster rect{fill:var(--md-mermaid-node-bg-color);stroke:var(--md-mermaid-node-fg-color)}.statediagram-state rect.divider{fill:var(--md-default-fg-color--lightest);stroke:var(--md-default-fg-color--lighter)}defs #statediagram-barbEnd{stroke:var(--md-mermaid-edge-color)}[id^=entity] path,[id^=entity] rect{fill:var(--md-default-bg-color)}.relationshipLine{stroke:var(--md-mermaid-edge-color)}defs .marker.oneOrMore.er *,defs .marker.onlyOne.er *,defs .marker.zeroOrMore.er *,defs .marker.zeroOrOne.er *{stroke:var(--md-mermaid-edge-color)!important}text:not([class]):last-child{fill:var(--md-mermaid-label-fg-color)}.actor{fill:var(--md-mermaid-sequence-actor-bg-color);stroke:var(--md-mermaid-sequence-actor-border-color)}text.actor>tspan{fill:var(--md-mermaid-sequence-actor-fg-color);font-family:var(--md-mermaid-font-family)}line{stroke:var(--md-mermaid-sequence-actor-line-color)}.actor-man circle,.actor-man line{fill:var(--md-mermaid-sequence-actorman-bg-color);stroke:var(--md-mermaid-sequence-actorman-line-color)}.messageLine0,.messageLine1{stroke:var(--md-mermaid-sequence-message-line-color)}.note{fill:var(--md-mermaid-sequence-note-bg-color);stroke:var(--md-mermaid-sequence-note-border-color)}.loopText,.loopText>tspan,.messageText,.noteText>tspan{stroke:none;font-family:var(--md-mermaid-font-family)!important}.messageText{fill:var(--md-mermaid-sequence-message-fg-color)}.loopText,.loopText>tspan{fill:var(--md-mermaid-sequence-loop-fg-color)}.noteText>tspan{fill:var(--md-mermaid-sequence-note-fg-color)}#arrowhead path{fill:var(--md-mermaid-sequence-message-line-color);stroke:none}.loopLine{fill:var(--md-mermaid-sequence-loop-bg-color);stroke:var(--md-mermaid-sequence-loop-border-color)}.labelBox{fill:var(--md-mermaid-sequence-label-bg-color);stroke:none}.labelText,.labelText>span{fill:var(--md-mermaid-sequence-label-fg-color);font-family:var(--md-mermaid-font-family)}.sequenceNumber{fill:var(--md-mermaid-sequence-number-fg-color)}rect.rect{fill:var(--md-mermaid-sequence-box-bg-color);stroke:none}rect.rect+text.text{fill:var(--md-mermaid-sequence-box-fg-color)}defs #sequencenumber{fill:var(--md-mermaid-sequence-number-bg-color)!important}";var so,is=0;function as(){return typeof mermaid=="undefined"||mermaid instanceof Element?_t("https://unpkg.com/mermaid@11/dist/mermaid.min.js"):$(void 0)}function Zn(e){return e.classList.remove("mermaid"),so||(so=as().pipe(O(()=>mermaid.initialize({startOnLoad:!1,themeCSS:Xn,sequence:{actorFontSize:"16px",messageFontSize:"16px",noteFontSize:"16px"}})),m(()=>{}),Z(1))),so.subscribe(()=>go(null,null,function*(){e.classList.add("mermaid");let t=`__mermaid_${is++}`,r=x("div",{class:"mermaid"}),o=e.textContent,{svg:n,fn:i}=yield mermaid.render(t,o),s=r.attachShadow({mode:"closed"});s.innerHTML=n,e.replaceWith(r),i==null||i(s)})),so.pipe(m(()=>({ref:e})))}var ei=x("table");function ti(e){return e.replaceWith(ei),ei.replaceWith(Un(e)),$({ref:e})}function ss(e){let t=e.find(r=>r.checked)||e[0];return L(...e.map(r=>h(r,"change").pipe(m(()=>j(`label[for="${r.id}"]`))))).pipe(Q(j(`label[for="${t.id}"]`)),m(r=>({active:r})))}function ri(e,{viewport$:t,target$:r}){let o=j(".tabbed-labels",e),n=M(":scope > input",e),i=no("prev");e.append(i);let s=no("next");return e.append(s),H(()=>{let a=new T,c=a.pipe(oe(),ae(!0));z([a,Le(e),mt(e)]).pipe(W(c),$e(1,ye)).subscribe({next([{active:p},l]){let f=Be(p),{width:u}=de(p);e.style.setProperty("--md-indicator-x",`${f.x}px`),e.style.setProperty("--md-indicator-width",`${u}px`);let d=gr(o);(f.xd.x+l.width)&&o.scrollTo({left:Math.max(0,f.x-16),behavior:"smooth"})},complete(){e.style.removeProperty("--md-indicator-x"),e.style.removeProperty("--md-indicator-width")}}),z([Ge(o),Le(o)]).pipe(W(c)).subscribe(([p,l])=>{let f=At(o);i.hidden=p.x<16,s.hidden=p.x>f.width-l.width-16}),L(h(i,"click").pipe(m(()=>-1)),h(s,"click").pipe(m(()=>1))).pipe(W(c)).subscribe(p=>{let{width:l}=de(o);o.scrollBy({left:l*p,behavior:"smooth"})}),r.pipe(W(c),g(p=>n.includes(p))).subscribe(p=>p.click()),o.classList.add("tabbed-labels--linked");for(let p of n){let l=j(`label[for="${p.id}"]`);l.replaceChildren(x("a",{href:`#${l.htmlFor}`,tabIndex:-1},...Array.from(l.childNodes))),h(l.firstElementChild,"click").pipe(W(c),g(f=>!(f.metaKey||f.ctrlKey)),O(f=>{f.preventDefault(),f.stopPropagation()})).subscribe(()=>{history.replaceState({},"",`#${l.htmlFor}`),l.click()})}return V("content.tabs.link")&&a.pipe(Ie(1),te(t)).subscribe(([{active:p},{offset:l}])=>{let f=p.innerText.trim();if(p.hasAttribute("data-md-switching"))p.removeAttribute("data-md-switching");else{let u=e.offsetTop-l.y;for(let v of M("[data-tabs]"))for(let S of M(":scope > input",v)){let X=j(`label[for="${S.id}"]`);if(X!==p&&X.innerText.trim()===f){X.setAttribute("data-md-switching",""),S.click();break}}window.scrollTo({top:e.offsetTop-u});let d=__md_get("__tabs")||[];__md_set("__tabs",[...new Set([f,...d])])}}),a.pipe(W(c)).subscribe(()=>{for(let p of M("audio, video",e))p.offsetWidth&&p.autoplay?p.play().catch(()=>{}):p.pause()}),ss(n).pipe(O(p=>a.next(p)),A(()=>a.complete()),m(p=>P({ref:e},p)))}).pipe(et(pe))}function oi(e,t){let{viewport$:r,target$:o,print$:n}=t;return L(...M(".annotate:not(.highlight)",e).map(i=>zn(i,{target$:o,print$:n})),...M("pre:not(.mermaid) > code",e).map(i=>Yn(i,{target$:o,print$:n})),...M("a",e).map(i=>Jn(i,t)),...M("pre.mermaid",e).map(i=>Zn(i)),...M("table:not([class])",e).map(i=>ti(i)),...M("details",e).map(i=>Bn(i,{target$:o,print$:n})),...M("[data-tabs]",e).map(i=>ri(i,{viewport$:r,target$:o})),...M("[title]:not([data-preview])",e).filter(()=>V("content.tooltips")).map(i=>Xe(i,{viewport$:r})),...M(".footnote-ref",e).filter(()=>V("content.footnote.tooltips")).map(i=>Vt(i,{content$:new F(s=>{let a=new URL(i.href).hash.slice(1),c=Array.from(document.getElementById(a).cloneNode(!0).children),p=wr(...c);return s.next(p),document.body.append(p),()=>p.remove()}),viewport$:r})))}function cs(e,{alert$:t}){return t.pipe(b(r=>L($(!0),$(!1).pipe(nt(2e3))).pipe(m(o=>({message:r,active:o})))))}function ni(e,t){let r=j(".md-typeset",e);return H(()=>{let o=new T;return o.subscribe(({message:n,active:i})=>{e.classList.toggle("md-dialog--active",i),r.textContent=n}),cs(e,t).pipe(O(n=>o.next(n)),A(()=>o.complete()),m(n=>P({ref:e},n)))})}var ps=0;function ls(e,t){document.body.append(e);let{width:r}=de(e);e.style.setProperty("--md-tooltip-width",`${r}px`),e.remove();let o=vr(t),n=typeof o!="undefined"?Ge(o):$({x:0,y:0}),i=L(Ye(t),it(t)).pipe(Y());return z([i,n]).pipe(m(([s,a])=>{let{x:c,y:p}=Be(t),l=de(t),f=t.closest("table");return f&&t.parentElement&&(c+=f.offsetLeft+t.parentElement.offsetLeft,p+=f.offsetTop+t.parentElement.offsetTop),{active:s,offset:{x:c-a.x+l.width/2-r/2,y:p-a.y+l.height+8}}}))}function ii(e){let t=e.title;if(!t.length)return y;let r=`__tooltip_${ps++}`,o=Dt(r,"inline"),n=j(".md-typeset",o);return n.innerHTML=t,H(()=>{let i=new T;return i.subscribe({next({offset:s}){o.style.setProperty("--md-tooltip-x",`${s.x}px`),o.style.setProperty("--md-tooltip-y",`${s.y}px`)},complete(){o.style.removeProperty("--md-tooltip-x"),o.style.removeProperty("--md-tooltip-y")}}),L(i.pipe(g(({active:s})=>s)),i.pipe(Ae(250),g(({active:s})=>!s))).subscribe({next({active:s}){s?(e.insertAdjacentElement("afterend",o),e.setAttribute("aria-describedby",r),e.removeAttribute("title")):(o.remove(),e.removeAttribute("aria-describedby"),e.setAttribute("title",t))},complete(){o.remove(),e.removeAttribute("aria-describedby"),e.setAttribute("title",t)}}),i.pipe($e(16,ye)).subscribe(({active:s})=>{o.classList.toggle("md-tooltip--active",s)}),i.pipe(gt(125,ye),g(()=>!!e.offsetParent),m(()=>e.offsetParent.getBoundingClientRect()),m(({x:s})=>s)).subscribe({next(s){s?o.style.setProperty("--md-tooltip-0",`${-s}px`):o.style.removeProperty("--md-tooltip-0")},complete(){o.style.removeProperty("--md-tooltip-0")}}),ls(o,e).pipe(O(s=>i.next(s)),A(()=>i.complete()),m(s=>P({ref:e},s)))}).pipe(et(pe))}function ms({viewport$:e}){if(!V("header.autohide"))return $(!1);let t=e.pipe(m(({offset:{y:n}})=>n),ot(2,1),m(([n,i])=>[nMath.abs(i-n.y)>100),m(([,[n]])=>n),Y()),o=Je("search");return z([e,o]).pipe(m(([{offset:n},i])=>n.y>400&&!i),Y(),b(n=>n?r:$(!1)),Q(!1))}function ai(e,t){return H(()=>z([Le(e),ms(t)])).pipe(m(([{height:r},o])=>({height:r,hidden:o})),Y((r,o)=>r.height===o.height&&r.hidden===o.hidden),Z(1))}function si(e,{header$:t,main$:r}){return H(()=>{let o=new T,n=o.pipe(oe(),ae(!0));o.pipe(ne("active"),Pe(t)).subscribe(([{active:s},{hidden:a}])=>{e.classList.toggle("md-header--shadow",s&&!a),e.hidden=a});let i=fe(M("[title]",e)).pipe(g(()=>V("content.tooltips")),J(s=>ii(s)));return r.subscribe(o),t.pipe(W(n),m(s=>P({ref:e},s)),Ve(i.pipe(W(n))))})}function fs(e,{viewport$:t,header$:r}){return Er(e,{viewport$:t,header$:r}).pipe(m(({offset:{y:o}})=>{let{height:n}=de(e);return{active:n>0&&o>=n}}),ne("active"))}function ci(e,t){return H(()=>{let r=new T;r.subscribe({next({active:n}){e.classList.toggle("md-header__title--active",n)},complete(){e.classList.remove("md-header__title--active")}});let o=ue(".md-content h1");return typeof o=="undefined"?y:fs(o,t).pipe(O(n=>r.next(n)),A(()=>r.complete()),m(n=>P({ref:e},n)))})}function pi(e,{viewport$:t,header$:r}){let o=r.pipe(m(({height:i})=>i),Y()),n=o.pipe(b(()=>Le(e).pipe(m(({height:i})=>({top:e.offsetTop,bottom:e.offsetTop+i})),ne("bottom"))));return z([o,n,t]).pipe(m(([i,{top:s,bottom:a},{offset:{y:c},size:{height:p}}])=>(p=Math.max(0,p-Math.max(0,s-c,i)-Math.max(0,p+c-a)),{offset:s-i,height:p,active:s-i<=c})),Y((i,s)=>i.offset===s.offset&&i.height===s.height&&i.active===s.active))}function us(e){let t=__md_get("__palette")||{index:e.findIndex(o=>matchMedia(o.getAttribute("data-md-color-media")).matches)},r=Math.max(0,Math.min(t.index,e.length-1));return $(...e).pipe(J(o=>h(o,"change").pipe(m(()=>o))),Q(e[r]),m(o=>({index:e.indexOf(o),color:{media:o.getAttribute("data-md-color-media"),scheme:o.getAttribute("data-md-color-scheme"),primary:o.getAttribute("data-md-color-primary"),accent:o.getAttribute("data-md-color-accent")}})),Z(1))}function li(e){let t=M("input",e),r=x("meta",{name:"theme-color"});document.head.appendChild(r);let o=x("meta",{name:"color-scheme"});document.head.appendChild(o);let n=Wt("(prefers-color-scheme: light)");return H(()=>{let i=new T;return i.subscribe(s=>{if(document.body.setAttribute("data-md-color-switching",""),s.color.media==="(prefers-color-scheme)"){let a=matchMedia("(prefers-color-scheme: light)"),c=document.querySelector(a.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");s.color.scheme=c.getAttribute("data-md-color-scheme"),s.color.primary=c.getAttribute("data-md-color-primary"),s.color.accent=c.getAttribute("data-md-color-accent")}for(let[a,c]of Object.entries(s.color))document.body.setAttribute(`data-md-color-${a}`,c);for(let a=0;as.key==="Enter"),te(i,(s,a)=>a)).subscribe(({index:s})=>{s=(s+1)%t.length,t[s].click(),t[s].focus()}),i.pipe(m(()=>{let s=Ce("header"),a=window.getComputedStyle(s);return o.content=a.colorScheme,a.backgroundColor.match(/\d+/g).map(c=>(+c).toString(16).padStart(2,"0")).join("")})).subscribe(s=>r.content=`#${s}`),i.pipe(xe(pe)).subscribe(()=>{document.body.removeAttribute("data-md-color-switching")}),us(t).pipe(W(n.pipe(Ie(1))),vt(),O(s=>i.next(s)),A(()=>i.complete()),m(s=>P({ref:e},s)))})}function mi(e,{progress$:t}){return H(()=>{let r=new T;return r.subscribe(({value:o})=>{e.style.setProperty("--md-progress-value",`${o}`)}),t.pipe(O(o=>r.next({value:o})),A(()=>r.complete()),m(o=>({ref:e,value:o})))})}function fi(e,t){return e.protocol=t.protocol,e.hostname=t.hostname,e}function ds(e,t){let r=new Map;for(let o of M("url",e)){let n=j("loc",o),i=[fi(new URL(n.textContent),t)];r.set(`${i[0]}`,i);for(let s of M("[rel=alternate]",o)){let a=s.getAttribute("href");a!=null&&i.push(fi(new URL(a),t))}}return r}function kt(e){return En(new URL("sitemap.xml",e)).pipe(m(t=>ds(t,new URL(e))),ve(()=>$(new Map)),le())}function ui({document$:e}){let t=new Map;e.pipe(b(()=>M("link[rel=alternate]")),m(r=>new URL(r.href)),g(r=>!t.has(r.toString())),J(r=>kt(r).pipe(m(o=>[r,o]),ve(()=>y)))).subscribe(([r,o])=>{t.set(r.toString().replace(/\/$/,""),o)}),h(document.body,"click").pipe(g(r=>!r.metaKey&&!r.ctrlKey),b(r=>{if(r.target instanceof Element){let o=r.target.closest("a");if(o&&!o.target){let n=[...t].find(([f])=>o.href.startsWith(`${f}/`));if(typeof n=="undefined")return y;let[i,s]=n,a=we();if(a.href.startsWith(i))return y;let c=Te(),p=a.href.replace(c.base,"");p=`${i}/${p}`;let l=s.has(p.split("#")[0])?new URL(p,c.base):new URL(i);return r.preventDefault(),$(l)}}return y})).subscribe(r=>st(r,!0))}var co=$t(ao());function hs(e){e.setAttribute("data-md-copying","");let t=e.closest("[data-copy]"),r=t?t.getAttribute("data-copy"):e.innerText;return e.removeAttribute("data-md-copying"),r.trimEnd()}function di({alert$:e}){co.default.isSupported()&&new F(t=>{new co.default("[data-clipboard-target], [data-clipboard-text]",{text:r=>r.getAttribute("data-clipboard-text")||hs(j(r.getAttribute("data-clipboard-target")))}).on("success",r=>t.next(r))}).pipe(O(t=>{t.trigger.focus()}),m(()=>Me("clipboard.copied"))).subscribe(e)}function hi(e,t){if(!(e.target instanceof Element))return y;let r=e.target.closest("a");if(r===null)return y;if(r.target||e.metaKey||e.ctrlKey)return y;let o=new URL(r.href);return o.search=o.hash="",t.has(`${o}`)?(e.preventDefault(),$(r)):y}function bi(e){let t=new Map;for(let r of M(":scope > *",e.head))t.set(r.outerHTML,r);return t}function vi(e){for(let t of M("[href], [src]",e))for(let r of["href","src"]){let o=t.getAttribute(r);if(o&&!/^(?:[a-z]+:)?\/\//i.test(o)){t[r]=t[r];break}}return $(e)}function bs(e){for(let o of["[data-md-component=announce]","[data-md-component=container]","[data-md-component=header-topic]","[data-md-component=outdated]","[data-md-component=logo]","[data-md-component=skip]",...V("navigation.tabs.sticky")?["[data-md-component=tabs]"]:[]]){let n=ue(o),i=ue(o,e);typeof n!="undefined"&&typeof i!="undefined"&&n.replaceWith(i)}let t=bi(document);for(let[o,n]of bi(e))t.has(o)?t.delete(o):document.head.appendChild(n);for(let o of t.values()){let n=o.getAttribute("name");n!=="theme-color"&&n!=="color-scheme"&&o.remove()}let r=Ce("container");return Ke(M("script",r)).pipe(b(o=>{let n=e.createElement("script");if(o.src){for(let i of o.getAttributeNames())n.setAttribute(i,o.getAttribute(i));return o.replaceWith(n),new F(i=>{n.onload=()=>i.complete()})}else return n.textContent=o.textContent,o.replaceWith(n),y}),oe(),ae(document))}function gi({sitemap$:e,location$:t,viewport$:r,progress$:o}){if(location.protocol==="file:")return y;$(document).subscribe(vi);let n=h(document.body,"click").pipe(Pe(e),b(([a,c])=>hi(a,c)),m(({href:a})=>new URL(a)),le()),i=h(window,"popstate").pipe(m(we),le());n.pipe(te(r)).subscribe(([a,{offset:c}])=>{history.replaceState(c,""),history.pushState(null,"",a)}),L(n,i).subscribe(t);let s=t.pipe(ne("pathname"),b(a=>xr(a,{progress$:o}).pipe(ve(()=>(st(a,!0),y)))),b(vi),b(bs),le());return L(s.pipe(te(t,(a,c)=>c)),s.pipe(b(()=>t),ne("hash")),t.pipe(Y((a,c)=>a.pathname===c.pathname&&a.hash===c.hash),b(()=>n),O(()=>history.back()))).subscribe(a=>{var c,p;history.state!==null||!a.hash?window.scrollTo(0,(p=(c=history.state)==null?void 0:c.y)!=null?p:0):(history.scrollRestoration="auto",gn(a.hash),history.scrollRestoration="manual")}),t.subscribe(()=>{history.scrollRestoration="manual"}),h(window,"beforeunload").subscribe(()=>{history.scrollRestoration="auto"}),r.pipe(ne("offset"),Ae(100)).subscribe(({offset:a})=>{history.replaceState(a,"")}),V("navigation.instant.prefetch")&&L(h(document.body,"mousemove"),h(document.body,"focusin")).pipe(Pe(e),b(([a,c])=>hi(a,c)),Ae(25),Qr(({href:a})=>a),hr(a=>{let c=document.createElement("link");return c.rel="prefetch",c.href=a.toString(),document.head.appendChild(c),h(c,"load").pipe(m(()=>c),Ee(1))})).subscribe(a=>a.remove()),s}var yi=$t(ro());function xi(e){let t=e.separator.split("|").map(n=>n.replace(/(\(\?[!=<][^)]+\))/g,"").length===0?"\uFFFD":n).join("|"),r=new RegExp(t,"img"),o=(n,i,s)=>`${i}${s}`;return n=>{n=n.replace(/[\s*+\-:~^]+/g," ").replace(/&/g,"&").trim();let i=new RegExp(`(^|${e.separator}|)(${n.replace(/[|\\{}()[\]^$+*?.-]/g,"\\$&").replace(r,"|")})`,"img");return s=>(0,yi.default)(s).replace(i,o).replace(/<\/mark>(\s+)]*>/img,"$1")}}function zt(e){return e.type===1}function Sr(e){return e.type===3}function Ei(e,t){let r=Mn(e);return L($(location.protocol!=="file:"),Je("search")).pipe(Re(o=>o),b(()=>t)).subscribe(({config:o,docs:n})=>r.next({type:0,data:{config:o,docs:n,options:{suggest:V("search.suggest")}}})),r}function wi(e){var l;let{selectedVersionSitemap:t,selectedVersionBaseURL:r,currentLocation:o,currentBaseURL:n}=e,i=(l=po(n))==null?void 0:l.pathname;if(i===void 0)return;let s=ys(o.pathname,i);if(s===void 0)return;let a=Es(t.keys());if(!t.has(a))return;let c=po(s,a);if(!c||!t.has(c.href))return;let p=po(s,r);if(p)return p.hash=o.hash,p.search=o.search,p}function po(e,t){try{return new URL(e,t)}catch(r){return}}function ys(e,t){if(e.startsWith(t))return e.slice(t.length)}function xs(e,t){let r=Math.min(e.length,t.length),o;for(o=0;oy)),o=r.pipe(m(n=>{let[,i]=t.base.match(/([^/]+)\/?$/);return n.find(({version:s,aliases:a})=>s===i||a.includes(i))||n[0]}));r.pipe(m(n=>new Map(n.map(i=>[`${new URL(`../${i.version}/`,t.base)}`,i]))),b(n=>h(document.body,"click").pipe(g(i=>!i.metaKey&&!i.ctrlKey),te(o),b(([i,s])=>{if(i.target instanceof Element){let a=i.target.closest("a");if(a&&!a.target&&n.has(a.href)){let c=a.href;return!i.target.closest(".md-version")&&n.get(c)===s?y:(i.preventDefault(),$(new URL(c)))}}return y}),b(i=>kt(i).pipe(m(s=>{var a;return(a=wi({selectedVersionSitemap:s,selectedVersionBaseURL:i,currentLocation:we(),currentBaseURL:t.base}))!=null?a:i})))))).subscribe(n=>st(n,!0)),z([r,o]).subscribe(([n,i])=>{j(".md-header__topic").appendChild(Wn(n,i))}),e.pipe(b(()=>o)).subscribe(n=>{var a;let i=new URL(t.base),s=__md_get("__outdated",sessionStorage,i);if(s===null){s=!0;let c=((a=t.version)==null?void 0:a.default)||"latest";Array.isArray(c)||(c=[c]);e:for(let p of c)for(let l of n.aliases.concat(n.version))if(new RegExp(p,"i").test(l)){s=!1;break e}__md_set("__outdated",s,sessionStorage,i)}if(s)for(let c of me("outdated"))c.hidden=!1})}function ws(e,{worker$:t}){let{searchParams:r}=we();r.has("q")&&(at("search",!0),e.value=r.get("q"),e.focus(),Je("search").pipe(Re(i=>!i)).subscribe(()=>{let i=we();i.searchParams.delete("q"),history.replaceState({},"",`${i}`)}));let o=Ye(e),n=L(t.pipe(Re(zt)),h(e,"keyup"),o).pipe(m(()=>e.value),Y());return z([n,o]).pipe(m(([i,s])=>({value:i,focus:s})),Z(1))}function Si(e,{worker$:t}){let r=new T,o=r.pipe(oe(),ae(!0));z([t.pipe(Re(zt)),r],(i,s)=>s).pipe(ne("value")).subscribe(({value:i})=>t.next({type:2,data:i})),r.pipe(ne("focus")).subscribe(({focus:i})=>{i&&at("search",i)}),h(e.form,"reset").pipe(W(o)).subscribe(()=>e.focus());let n=j("header [for=__search]");return h(n,"click").subscribe(()=>e.focus()),ws(e,{worker$:t}).pipe(O(i=>r.next(i)),A(()=>r.complete()),m(i=>P({ref:e},i)),Z(1))}function Oi(e,{worker$:t,query$:r}){let o=new T,n=un(e.parentElement).pipe(g(Boolean)),i=e.parentElement,s=j(":scope > :first-child",e),a=j(":scope > :last-child",e);Je("search").subscribe(l=>{a.setAttribute("role",l?"list":"presentation"),a.hidden=!l}),o.pipe(te(r),Gr(t.pipe(Re(zt)))).subscribe(([{items:l},{value:f}])=>{switch(l.length){case 0:s.textContent=f.length?Me("search.result.none"):Me("search.result.placeholder");break;case 1:s.textContent=Me("search.result.one");break;default:let u=br(l.length);s.textContent=Me("search.result.other",u)}});let c=o.pipe(O(()=>a.innerHTML=""),b(({items:l})=>L($(...l.slice(0,10)),$(...l.slice(10)).pipe(ot(4),Xr(n),b(([f])=>f)))),m(Fn),le());return c.subscribe(l=>a.appendChild(l)),c.pipe(J(l=>{let f=ue("details",l);return typeof f=="undefined"?y:h(f,"toggle").pipe(W(o),m(()=>f))})).subscribe(l=>{l.open===!1&&l.offsetTop<=i.scrollTop&&i.scrollTo({top:l.offsetTop})}),t.pipe(g(Sr),m(({data:l})=>l)).pipe(O(l=>o.next(l)),A(()=>o.complete()),m(l=>P({ref:e},l)))}function Ts(e,{query$:t}){return t.pipe(m(({value:r})=>{let o=we();return o.hash="",r=r.replace(/\s+/g,"+").replace(/&/g,"%26").replace(/=/g,"%3D"),o.search=`q=${r}`,{url:o}}))}function Li(e,t){let r=new T,o=r.pipe(oe(),ae(!0));return r.subscribe(({url:n})=>{e.setAttribute("data-clipboard-text",e.href),e.href=`${n}`}),h(e,"click").pipe(W(o)).subscribe(n=>n.preventDefault()),Ts(e,t).pipe(O(n=>r.next(n)),A(()=>r.complete()),m(n=>P({ref:e},n)))}function Mi(e,{worker$:t,keyboard$:r}){let o=new T,n=Ce("search-query"),i=L(h(n,"keydown"),h(n,"focus")).pipe(xe(pe),m(()=>n.value),Y());return o.pipe(Pe(i),m(([{suggest:a},c])=>{let p=c.split(/([\s-]+)/);if(a!=null&&a.length&&p[p.length-1]){let l=a[a.length-1];l.startsWith(p[p.length-1])&&(p[p.length-1]=l)}else p.length=0;return p})).subscribe(a=>e.innerHTML=a.join("").replace(/\s/g," ")),r.pipe(g(({mode:a})=>a==="search")).subscribe(a=>{a.type==="ArrowRight"&&e.innerText.length&&n.selectionStart===n.value.length&&(n.value=e.innerText)}),t.pipe(g(Sr),m(({data:a})=>a)).pipe(O(a=>o.next(a)),A(()=>o.complete()),m(()=>({ref:e})))}function _i(e,{index$:t,keyboard$:r}){let o=Te();try{let n=Ei(o.search,t),i=Ce("search-query",e),s=Ce("search-result",e);h(e,"click").pipe(g(({target:c})=>c instanceof Element&&!!c.closest("a"))).subscribe(()=>at("search",!1)),r.pipe(g(({mode:c})=>c==="search")).subscribe(c=>{let p=Ne();switch(c.type){case"Enter":if(p===i){let l=new Map;for(let f of M(":first-child [href]",s)){let u=f.firstElementChild;l.set(f,parseFloat(u.getAttribute("data-md-score")))}if(l.size){let[[f]]=[...l].sort(([,u],[,d])=>d-u);f.click()}c.claim()}break;case"Escape":case"Tab":at("search",!1),i.blur();break;case"ArrowUp":case"ArrowDown":if(typeof p=="undefined")i.focus();else{let l=[i,...M(":not(details) > [href], summary, details[open] [href]",s)],f=Math.max(0,(Math.max(0,l.indexOf(p))+l.length+(c.type==="ArrowUp"?-1:1))%l.length);l[f].focus()}c.claim();break;default:i!==Ne()&&i.focus()}}),r.pipe(g(({mode:c})=>c==="global")).subscribe(c=>{switch(c.type){case"f":case"s":case"/":i.focus(),i.select(),c.claim();break}});let a=Si(i,{worker$:n});return L(a,Oi(s,{worker$:n,query$:a})).pipe(Ve(...me("search-share",e).map(c=>Li(c,{query$:a})),...me("search-suggest",e).map(c=>Mi(c,{worker$:n,keyboard$:r}))))}catch(n){return e.hidden=!0,tt}}function Ai(e,{index$:t,location$:r}){return z([t,r.pipe(Q(we()),g(o=>!!o.searchParams.get("h")))]).pipe(m(([o,n])=>xi(o.config)(n.searchParams.get("h"))),m(o=>{var s;let n=new Map,i=document.createNodeIterator(e,NodeFilter.SHOW_TEXT);for(let a=i.nextNode();a;a=i.nextNode())if((s=a.parentElement)!=null&&s.offsetHeight){let c=a.textContent,p=o(c);p.length>c.length&&n.set(a,p)}for(let[a,c]of n){let{childNodes:p}=x("span",null,c);a.replaceWith(...Array.from(p))}return{ref:e,nodes:n}}))}function Ss(e,{viewport$:t,main$:r}){let o=e.closest(".md-grid"),n=o.offsetTop-o.parentElement.offsetTop;return z([r,t]).pipe(m(([{offset:i,height:s},{offset:{y:a}}])=>(s=s+Math.min(n,Math.max(0,a-i))-n,{height:s,locked:a>=i+n})),Y((i,s)=>i.height===s.height&&i.locked===s.locked))}function lo(e,o){var n=o,{header$:t}=n,r=vo(n,["header$"]);let i=j(".md-sidebar__scrollwrap",e),{y:s}=Be(i);return H(()=>{let a=new T,c=a.pipe(oe(),ae(!0)),p=a.pipe($e(0,ye));return p.pipe(te(t)).subscribe({next([{height:l},{height:f}]){i.style.height=`${l-2*s}px`,e.style.top=`${f}px`},complete(){i.style.height="",e.style.top=""}}),p.pipe(Re()).subscribe(()=>{for(let l of M(".md-nav__link--active[href]",e)){if(!l.clientHeight)continue;let f=l.closest(".md-sidebar__scrollwrap");if(typeof f!="undefined"){let u=l.offsetTop-f.offsetTop,{height:d}=de(f);f.scrollTo({top:u-d/2})}}}),fe(M("label[tabindex]",e)).pipe(J(l=>h(l,"click").pipe(xe(pe),m(()=>l),W(c)))).subscribe(l=>{let f=j(`[id="${l.htmlFor}"]`);j(`[aria-labelledby="${l.id}"]`).setAttribute("aria-expanded",`${f.checked}`)}),V("content.tooltips")&&fe(M("abbr[title]",e)).pipe(J(l=>Xe(l,{viewport$})),W(c)).subscribe(),Ss(e,r).pipe(O(l=>a.next(l)),A(()=>a.complete()),m(l=>P({ref:e},l)))})}function Ci(e,t){if(typeof t!="undefined"){let r=`https://api.github.com/repos/${e}/${t}`;return rt(ze(`${r}/releases/latest`).pipe(ve(()=>y),m(o=>({version:o.tag_name})),Qe({})),ze(r).pipe(ve(()=>y),m(o=>({stars:o.stargazers_count,forks:o.forks_count})),Qe({}))).pipe(m(([o,n])=>P(P({},o),n)))}else{let r=`https://api.github.com/users/${e}`;return ze(r).pipe(m(o=>({repositories:o.public_repos})),Qe({}))}}function ki(e,t){let r=`https://${e}/api/v4/projects/${encodeURIComponent(t)}`;return rt(ze(`${r}/releases/permalink/latest`).pipe(ve(()=>y),m(({tag_name:o})=>({version:o})),Qe({})),ze(r).pipe(ve(()=>y),m(({star_count:o,forks_count:n})=>({stars:o,forks:n})),Qe({}))).pipe(m(([o,n])=>P(P({},o),n)))}function Hi(e){let t=e.match(/^.+github\.com\/([^/]+)\/?([^/]+)?/i);if(t){let[,r,o]=t;return Ci(r,o)}if(t=e.match(/^.+?([^/]*gitlab[^/]+)\/(.+?)\/?$/i),t){let[,r,o]=t;return ki(r,o)}return y}var Os;function Ls(e){return Os||(Os=H(()=>{let t=__md_get("__source",sessionStorage);if(t)return $(t);if(me("consent").length){let o=__md_get("__consent");if(!(o&&o.github))return y}return Hi(e.href).pipe(O(o=>__md_set("__source",o,sessionStorage)))}).pipe(ve(()=>y),g(t=>Object.keys(t).length>0),m(t=>({facts:t})),Z(1)))}function $i(e){let t=j(":scope > :last-child",e);return H(()=>{let r=new T;return r.subscribe(({facts:o})=>{t.appendChild(jn(o)),t.classList.add("md-source__repository--active")}),Ls(e).pipe(O(o=>r.next(o)),A(()=>r.complete()),m(o=>P({ref:e},o)))})}function Ms(e,{viewport$:t,header$:r}){return Le(document.body).pipe(b(()=>Er(e,{header$:r,viewport$:t})),m(({offset:{y:o}})=>({hidden:o>=10})),ne("hidden"))}function Pi(e,t){return H(()=>{let r=new T;return r.subscribe({next({hidden:o}){e.hidden=o},complete(){e.hidden=!1}}),(V("navigation.tabs.sticky")?$({hidden:!1}):Ms(e,t)).pipe(O(o=>r.next(o)),A(()=>r.complete()),m(o=>P({ref:e},o)))})}function _s(e,{viewport$:t,header$:r}){let o=new Map,n=M(".md-nav__link",e);for(let a of n){let c=decodeURIComponent(a.hash.substring(1)),p=ue(`[id="${c}"]`);typeof p!="undefined"&&o.set(a,p)}let i=r.pipe(ne("height"),m(({height:a})=>{let c=Ce("main"),p=j(":scope > :first-child",c);return a+.8*(p.offsetTop-c.offsetTop)}),le());return Le(document.body).pipe(ne("height"),b(a=>H(()=>{let c=[];return $([...o].reduce((p,[l,f])=>{for(;c.length&&o.get(c[c.length-1]).tagName>=f.tagName;)c.pop();let u=f.offsetTop;for(;!u&&f.parentElement;)f=f.parentElement,u=f.offsetTop;let d=f.offsetParent;for(;d;d=d.offsetParent)u+=d.offsetTop;return p.set([...c=[...c,l]].reverse(),u)},new Map))}).pipe(m(c=>new Map([...c].sort(([,p],[,l])=>p-l))),Pe(i),b(([c,p])=>t.pipe(Ut(([l,f],{offset:{y:u},size:d})=>{let v=u+d.height>=Math.floor(a.height);for(;f.length;){let[,S]=f[0];if(S-p=u&&!v)f=[l.pop(),...f];else break}return[l,f]},[[],[...c]]),Y((l,f)=>l[0]===f[0]&&l[1]===f[1])))))).pipe(m(([a,c])=>({prev:a.map(([p])=>p),next:c.map(([p])=>p)})),Q({prev:[],next:[]}),ot(2,1),m(([a,c])=>a.prev.length{let i=new T,s=i.pipe(oe(),ae(!0));if(i.subscribe(({prev:a,next:c})=>{for(let[p]of c)p.classList.remove("md-nav__link--passed"),p.classList.remove("md-nav__link--active");for(let[p,[l]]of a.entries())l.classList.add("md-nav__link--passed"),l.classList.toggle("md-nav__link--active",p===a.length-1)}),V("toc.follow")){let a=L(t.pipe(Ae(1),m(()=>{})),t.pipe(Ae(250),m(()=>"smooth")));i.pipe(g(({prev:c})=>c.length>0),Pe(o.pipe(xe(pe))),te(a)).subscribe(([[{prev:c}],p])=>{let[l]=c[c.length-1];if(l.offsetHeight){let f=vr(l);if(typeof f!="undefined"){let u=l.offsetTop-f.offsetTop,{height:d}=de(f);f.scrollTo({top:u-d/2,behavior:p})}}})}return V("navigation.tracking")&&t.pipe(W(s),ne("offset"),Ae(250),Ie(1),W(n.pipe(Ie(1))),vt({delay:250}),te(i)).subscribe(([,{prev:a}])=>{let c=we(),p=a[a.length-1];if(p&&p.length){let[l]=p,{hash:f}=new URL(l.href);c.hash!==f&&(c.hash=f,history.replaceState({},"",`${c}`))}else c.hash="",history.replaceState({},"",`${c}`)}),_s(e,{viewport$:t,header$:r}).pipe(O(a=>i.next(a)),A(()=>i.complete()),m(a=>P({ref:e},a)))})}function As(e,{viewport$:t,main$:r,target$:o}){let n=t.pipe(m(({offset:{y:s}})=>s),ot(2,1),m(([s,a])=>s>a&&a>0),Y()),i=r.pipe(m(({active:s})=>s));return z([i,n]).pipe(m(([s,a])=>!(s&&a)),Y(),W(o.pipe(Ie(1))),ae(!0),vt({delay:250}),m(s=>({hidden:s})))}function Ii(e,{viewport$:t,header$:r,main$:o,target$:n}){let i=new T,s=i.pipe(oe(),ae(!0));return i.subscribe({next({hidden:a}){e.hidden=a,a?(e.setAttribute("tabindex","-1"),e.blur()):e.removeAttribute("tabindex")},complete(){e.style.top="",e.hidden=!0,e.removeAttribute("tabindex")}}),r.pipe(W(s),ne("height")).subscribe(({height:a})=>{e.style.top=`${a+16}px`}),h(e,"click").subscribe(a=>{a.preventDefault(),window.scrollTo({top:0})}),As(e,{viewport$:t,main$:o,target$:n}).pipe(O(a=>i.next(a)),A(()=>i.complete()),m(a=>P({ref:e},a)))}function Fi({document$:e,viewport$:t}){e.pipe(b(()=>M(".md-ellipsis")),J(r=>mt(r).pipe(W(e.pipe(Ie(1))),g(o=>o),m(()=>r),Ee(1))),g(r=>r.offsetWidth{let o=r.innerText,n=r.closest("a")||r;return n.title=o,V("content.tooltips")?Xe(n,{viewport$:t}).pipe(W(e.pipe(Ie(1))),A(()=>n.removeAttribute("title"))):y})).subscribe(),V("content.tooltips")&&e.pipe(b(()=>M(".md-status")),J(r=>Xe(r,{viewport$:t}))).subscribe()}function ji({document$:e,tablet$:t}){e.pipe(b(()=>M(".md-toggle--indeterminate")),O(r=>{r.indeterminate=!0,r.checked=!1}),J(r=>h(r,"change").pipe(Jr(()=>r.classList.contains("md-toggle--indeterminate")),m(()=>r))),te(t)).subscribe(([r,o])=>{r.classList.remove("md-toggle--indeterminate"),o&&(r.checked=!1)})}function Cs(){return/(iPad|iPhone|iPod)/.test(navigator.userAgent)}function Ui({document$:e}){e.pipe(b(()=>M("[data-md-scrollfix]")),O(t=>t.removeAttribute("data-md-scrollfix")),g(Cs),J(t=>h(t,"touchstart").pipe(m(()=>t)))).subscribe(t=>{let r=t.scrollTop;r===0?t.scrollTop=1:r+t.offsetHeight===t.scrollHeight&&(t.scrollTop=r-1)})}function Wi({viewport$:e,tablet$:t}){z([Je("search"),t]).pipe(m(([r,o])=>r&&!o),b(r=>$(r).pipe(nt(r?400:100))),te(e)).subscribe(([r,{offset:{y:o}}])=>{if(r)document.body.setAttribute("data-md-scrolllock",""),document.body.style.top=`-${o}px`;else{let n=-1*parseInt(document.body.style.top,10);document.body.removeAttribute("data-md-scrolllock"),document.body.style.top="",n&&window.scrollTo(0,n)}})}Object.entries||(Object.entries=function(e){let t=[];for(let r of Object.keys(e))t.push([r,e[r]]);return t});Object.values||(Object.values=function(e){let t=[];for(let r of Object.keys(e))t.push(e[r]);return t});typeof Element!="undefined"&&(Element.prototype.scrollTo||(Element.prototype.scrollTo=function(e,t){typeof e=="object"?(this.scrollLeft=e.left,this.scrollTop=e.top):(this.scrollLeft=e,this.scrollTop=t)}),Element.prototype.replaceWith||(Element.prototype.replaceWith=function(...e){let t=this.parentNode;if(t){e.length===0&&t.removeChild(this);for(let r=e.length-1;r>=0;r--){let o=e[r];typeof o=="string"?o=document.createTextNode(o):o.parentNode&&o.parentNode.removeChild(o),r?t.insertBefore(this.previousSibling,o):t.replaceChild(o,this)}}}));function ks(){return location.protocol==="file:"?_t(`${new URL("search/search_index.js",Or.base)}`).pipe(m(()=>__index),Z(1)):ze(new URL("search/search_index.json",Or.base))}document.documentElement.classList.remove("no-js");document.documentElement.classList.add("js");var ct=an(),Kt=bn(),Ht=yn(Kt),mo=hn(),ke=Ln(),Lr=Wt("(min-width: 60em)"),Vi=Wt("(min-width: 76.25em)"),Ni=xn(),Or=Te(),zi=document.forms.namedItem("search")?ks():tt,fo=new T;di({alert$:fo});ui({document$:ct});var uo=new T,qi=kt(Or.base);V("navigation.instant")&&gi({sitemap$:qi,location$:Kt,viewport$:ke,progress$:uo}).subscribe(ct);var Di;((Di=Or.version)==null?void 0:Di.provider)==="mike"&&Ti({document$:ct});L(Kt,Ht).pipe(nt(125)).subscribe(()=>{at("drawer",!1),at("search",!1)});mo.pipe(g(({mode:e})=>e==="global")).subscribe(e=>{switch(e.type){case"p":case",":let t=ue("link[rel=prev]");typeof t!="undefined"&&st(t);break;case"n":case".":let r=ue("link[rel=next]");typeof r!="undefined"&&st(r);break;case"Enter":let o=Ne();o instanceof HTMLLabelElement&&o.click()}});Fi({viewport$:ke,document$:ct});ji({document$:ct,tablet$:Lr});Ui({document$:ct});Wi({viewport$:ke,tablet$:Lr});var ft=ai(Ce("header"),{viewport$:ke}),qt=ct.pipe(m(()=>Ce("main")),b(e=>pi(e,{viewport$:ke,header$:ft})),Z(1)),Hs=L(...me("consent").map(e=>An(e,{target$:Ht})),...me("dialog").map(e=>ni(e,{alert$:fo})),...me("palette").map(e=>li(e)),...me("progress").map(e=>mi(e,{progress$:uo})),...me("search").map(e=>_i(e,{index$:zi,keyboard$:mo})),...me("source").map(e=>$i(e))),$s=H(()=>L(...me("announce").map(e=>_n(e)),...me("content").map(e=>oi(e,{sitemap$:qi,viewport$:ke,target$:Ht,print$:Ni})),...me("content").map(e=>V("search.highlight")?Ai(e,{index$:zi,location$:Kt}):y),...me("header").map(e=>si(e,{viewport$:ke,header$:ft,main$:qt})),...me("header-title").map(e=>ci(e,{viewport$:ke,header$:ft})),...me("sidebar").map(e=>e.getAttribute("data-md-type")==="navigation"?eo(Vi,()=>lo(e,{viewport$:ke,header$:ft,main$:qt})):eo(Lr,()=>lo(e,{viewport$:ke,header$:ft,main$:qt}))),...me("tabs").map(e=>Pi(e,{viewport$:ke,header$:ft})),...me("toc").map(e=>Ri(e,{viewport$:ke,header$:ft,main$:qt,target$:Ht})),...me("top").map(e=>Ii(e,{viewport$:ke,header$:ft,main$:qt,target$:Ht})))),Ki=ct.pipe(b(()=>$s),Ve(Hs),Z(1));Ki.subscribe();window.document$=ct;window.location$=Kt;window.target$=Ht;window.keyboard$=mo;window.viewport$=ke;window.tablet$=Lr;window.screen$=Vi;window.print$=Ni;window.alert$=fo;window.progress$=uo;window.component$=Ki;})(); +//# sourceMappingURL=bundle.79ae519e.min.js.map + diff --git a/assets/javascripts/bundle.79ae519e.min.js.map b/assets/javascripts/bundle.79ae519e.min.js.map new file mode 100644 index 00000000..5cf02892 --- /dev/null +++ b/assets/javascripts/bundle.79ae519e.min.js.map @@ -0,0 +1,7 @@ +{ + "version": 3, + "sources": ["node_modules/focus-visible/dist/focus-visible.js", "node_modules/escape-html/index.js", "node_modules/clipboard/dist/clipboard.js", "src/templates/assets/javascripts/bundle.ts", "node_modules/tslib/tslib.es6.mjs", "node_modules/rxjs/src/internal/util/isFunction.ts", "node_modules/rxjs/src/internal/util/createErrorClass.ts", "node_modules/rxjs/src/internal/util/UnsubscriptionError.ts", "node_modules/rxjs/src/internal/util/arrRemove.ts", "node_modules/rxjs/src/internal/Subscription.ts", "node_modules/rxjs/src/internal/config.ts", "node_modules/rxjs/src/internal/scheduler/timeoutProvider.ts", "node_modules/rxjs/src/internal/util/reportUnhandledError.ts", "node_modules/rxjs/src/internal/util/noop.ts", "node_modules/rxjs/src/internal/NotificationFactories.ts", "node_modules/rxjs/src/internal/util/errorContext.ts", "node_modules/rxjs/src/internal/Subscriber.ts", "node_modules/rxjs/src/internal/symbol/observable.ts", "node_modules/rxjs/src/internal/util/identity.ts", "node_modules/rxjs/src/internal/util/pipe.ts", "node_modules/rxjs/src/internal/Observable.ts", "node_modules/rxjs/src/internal/util/lift.ts", "node_modules/rxjs/src/internal/operators/OperatorSubscriber.ts", "node_modules/rxjs/src/internal/scheduler/animationFrameProvider.ts", "node_modules/rxjs/src/internal/util/ObjectUnsubscribedError.ts", "node_modules/rxjs/src/internal/Subject.ts", "node_modules/rxjs/src/internal/BehaviorSubject.ts", "node_modules/rxjs/src/internal/scheduler/dateTimestampProvider.ts", "node_modules/rxjs/src/internal/ReplaySubject.ts", "node_modules/rxjs/src/internal/scheduler/Action.ts", "node_modules/rxjs/src/internal/scheduler/intervalProvider.ts", "node_modules/rxjs/src/internal/scheduler/AsyncAction.ts", "node_modules/rxjs/src/internal/Scheduler.ts", "node_modules/rxjs/src/internal/scheduler/AsyncScheduler.ts", "node_modules/rxjs/src/internal/scheduler/async.ts", "node_modules/rxjs/src/internal/scheduler/QueueAction.ts", "node_modules/rxjs/src/internal/scheduler/QueueScheduler.ts", "node_modules/rxjs/src/internal/scheduler/queue.ts", "node_modules/rxjs/src/internal/scheduler/AnimationFrameAction.ts", "node_modules/rxjs/src/internal/scheduler/AnimationFrameScheduler.ts", "node_modules/rxjs/src/internal/scheduler/animationFrame.ts", "node_modules/rxjs/src/internal/observable/empty.ts", "node_modules/rxjs/src/internal/util/isScheduler.ts", "node_modules/rxjs/src/internal/util/args.ts", "node_modules/rxjs/src/internal/util/isArrayLike.ts", "node_modules/rxjs/src/internal/util/isPromise.ts", "node_modules/rxjs/src/internal/util/isInteropObservable.ts", "node_modules/rxjs/src/internal/util/isAsyncIterable.ts", "node_modules/rxjs/src/internal/util/throwUnobservableError.ts", "node_modules/rxjs/src/internal/symbol/iterator.ts", "node_modules/rxjs/src/internal/util/isIterable.ts", "node_modules/rxjs/src/internal/util/isReadableStreamLike.ts", "node_modules/rxjs/src/internal/observable/innerFrom.ts", "node_modules/rxjs/src/internal/util/executeSchedule.ts", "node_modules/rxjs/src/internal/operators/observeOn.ts", "node_modules/rxjs/src/internal/operators/subscribeOn.ts", "node_modules/rxjs/src/internal/scheduled/scheduleObservable.ts", "node_modules/rxjs/src/internal/scheduled/schedulePromise.ts", "node_modules/rxjs/src/internal/scheduled/scheduleArray.ts", "node_modules/rxjs/src/internal/scheduled/scheduleIterable.ts", "node_modules/rxjs/src/internal/scheduled/scheduleAsyncIterable.ts", "node_modules/rxjs/src/internal/scheduled/scheduleReadableStreamLike.ts", "node_modules/rxjs/src/internal/scheduled/scheduled.ts", "node_modules/rxjs/src/internal/observable/from.ts", "node_modules/rxjs/src/internal/observable/of.ts", "node_modules/rxjs/src/internal/observable/throwError.ts", "node_modules/rxjs/src/internal/util/EmptyError.ts", "node_modules/rxjs/src/internal/util/isDate.ts", "node_modules/rxjs/src/internal/operators/map.ts", "node_modules/rxjs/src/internal/util/mapOneOrManyArgs.ts", "node_modules/rxjs/src/internal/util/argsArgArrayOrObject.ts", "node_modules/rxjs/src/internal/util/createObject.ts", "node_modules/rxjs/src/internal/observable/combineLatest.ts", "node_modules/rxjs/src/internal/operators/mergeInternals.ts", "node_modules/rxjs/src/internal/operators/mergeMap.ts", "node_modules/rxjs/src/internal/operators/mergeAll.ts", "node_modules/rxjs/src/internal/operators/concatAll.ts", "node_modules/rxjs/src/internal/observable/concat.ts", "node_modules/rxjs/src/internal/observable/defer.ts", "node_modules/rxjs/src/internal/observable/fromEvent.ts", "node_modules/rxjs/src/internal/observable/fromEventPattern.ts", "node_modules/rxjs/src/internal/observable/timer.ts", "node_modules/rxjs/src/internal/observable/merge.ts", "node_modules/rxjs/src/internal/observable/never.ts", "node_modules/rxjs/src/internal/util/argsOrArgArray.ts", "node_modules/rxjs/src/internal/operators/filter.ts", "node_modules/rxjs/src/internal/observable/zip.ts", "node_modules/rxjs/src/internal/operators/audit.ts", "node_modules/rxjs/src/internal/operators/auditTime.ts", "node_modules/rxjs/src/internal/operators/bufferCount.ts", "node_modules/rxjs/src/internal/operators/catchError.ts", "node_modules/rxjs/src/internal/operators/scanInternals.ts", "node_modules/rxjs/src/internal/operators/combineLatest.ts", "node_modules/rxjs/src/internal/operators/combineLatestWith.ts", "node_modules/rxjs/src/internal/operators/debounce.ts", "node_modules/rxjs/src/internal/operators/debounceTime.ts", "node_modules/rxjs/src/internal/operators/defaultIfEmpty.ts", "node_modules/rxjs/src/internal/operators/take.ts", "node_modules/rxjs/src/internal/operators/ignoreElements.ts", "node_modules/rxjs/src/internal/operators/mapTo.ts", "node_modules/rxjs/src/internal/operators/delayWhen.ts", "node_modules/rxjs/src/internal/operators/delay.ts", "node_modules/rxjs/src/internal/operators/distinct.ts", "node_modules/rxjs/src/internal/operators/distinctUntilChanged.ts", "node_modules/rxjs/src/internal/operators/distinctUntilKeyChanged.ts", "node_modules/rxjs/src/internal/operators/throwIfEmpty.ts", "node_modules/rxjs/src/internal/operators/endWith.ts", "node_modules/rxjs/src/internal/operators/exhaustMap.ts", "node_modules/rxjs/src/internal/operators/finalize.ts", "node_modules/rxjs/src/internal/operators/first.ts", "node_modules/rxjs/src/internal/operators/takeLast.ts", "node_modules/rxjs/src/internal/operators/merge.ts", "node_modules/rxjs/src/internal/operators/mergeWith.ts", "node_modules/rxjs/src/internal/operators/repeat.ts", "node_modules/rxjs/src/internal/operators/scan.ts", "node_modules/rxjs/src/internal/operators/share.ts", "node_modules/rxjs/src/internal/operators/shareReplay.ts", "node_modules/rxjs/src/internal/operators/skip.ts", "node_modules/rxjs/src/internal/operators/skipUntil.ts", "node_modules/rxjs/src/internal/operators/startWith.ts", "node_modules/rxjs/src/internal/operators/switchMap.ts", "node_modules/rxjs/src/internal/operators/takeUntil.ts", "node_modules/rxjs/src/internal/operators/takeWhile.ts", "node_modules/rxjs/src/internal/operators/tap.ts", "node_modules/rxjs/src/internal/operators/throttle.ts", "node_modules/rxjs/src/internal/operators/throttleTime.ts", "node_modules/rxjs/src/internal/operators/withLatestFrom.ts", "node_modules/rxjs/src/internal/operators/zip.ts", "node_modules/rxjs/src/internal/operators/zipWith.ts", "src/templates/assets/javascripts/browser/document/index.ts", "src/templates/assets/javascripts/browser/element/_/index.ts", "src/templates/assets/javascripts/browser/element/focus/index.ts", "src/templates/assets/javascripts/browser/element/hover/index.ts", "src/templates/assets/javascripts/utilities/h/index.ts", "src/templates/assets/javascripts/utilities/round/index.ts", "src/templates/assets/javascripts/browser/script/index.ts", "src/templates/assets/javascripts/browser/element/size/_/index.ts", "src/templates/assets/javascripts/browser/element/size/content/index.ts", "src/templates/assets/javascripts/browser/element/offset/_/index.ts", "src/templates/assets/javascripts/browser/element/offset/content/index.ts", "src/templates/assets/javascripts/browser/element/visibility/index.ts", "src/templates/assets/javascripts/browser/toggle/index.ts", "src/templates/assets/javascripts/browser/keyboard/index.ts", "src/templates/assets/javascripts/browser/location/_/index.ts", "src/templates/assets/javascripts/browser/location/hash/index.ts", "src/templates/assets/javascripts/browser/media/index.ts", "src/templates/assets/javascripts/browser/request/index.ts", "src/templates/assets/javascripts/browser/viewport/offset/index.ts", "src/templates/assets/javascripts/browser/viewport/size/index.ts", "src/templates/assets/javascripts/browser/viewport/_/index.ts", "src/templates/assets/javascripts/browser/viewport/at/index.ts", "src/templates/assets/javascripts/browser/worker/index.ts", "src/templates/assets/javascripts/_/index.ts", "src/templates/assets/javascripts/components/_/index.ts", "src/templates/assets/javascripts/components/announce/index.ts", "src/templates/assets/javascripts/components/consent/index.ts", "src/templates/assets/javascripts/templates/tooltip/index.tsx", "src/templates/assets/javascripts/templates/annotation/index.tsx", "src/templates/assets/javascripts/templates/clipboard/index.tsx", "src/templates/assets/javascripts/templates/search/index.tsx", "src/templates/assets/javascripts/templates/source/index.tsx", "src/templates/assets/javascripts/templates/tabbed/index.tsx", "src/templates/assets/javascripts/templates/table/index.tsx", "src/templates/assets/javascripts/templates/version/index.tsx", "src/templates/assets/javascripts/components/tooltip2/index.ts", "src/templates/assets/javascripts/components/content/annotation/_/index.ts", "src/templates/assets/javascripts/components/content/annotation/list/index.ts", "src/templates/assets/javascripts/components/content/annotation/block/index.ts", "src/templates/assets/javascripts/components/content/code/_/index.ts", "src/templates/assets/javascripts/components/content/details/index.ts", "src/templates/assets/javascripts/components/content/link/index.ts", "src/templates/assets/javascripts/components/content/mermaid/index.css", "src/templates/assets/javascripts/components/content/mermaid/index.ts", "src/templates/assets/javascripts/components/content/table/index.ts", "src/templates/assets/javascripts/components/content/tabs/index.ts", "src/templates/assets/javascripts/components/content/_/index.ts", "src/templates/assets/javascripts/components/dialog/index.ts", "src/templates/assets/javascripts/components/tooltip/index.ts", "src/templates/assets/javascripts/components/header/_/index.ts", "src/templates/assets/javascripts/components/header/title/index.ts", "src/templates/assets/javascripts/components/main/index.ts", "src/templates/assets/javascripts/components/palette/index.ts", "src/templates/assets/javascripts/components/progress/index.ts", "src/templates/assets/javascripts/integrations/sitemap/index.ts", "src/templates/assets/javascripts/integrations/alternate/index.ts", "src/templates/assets/javascripts/integrations/clipboard/index.ts", "src/templates/assets/javascripts/integrations/instant/index.ts", "src/templates/assets/javascripts/integrations/search/highlighter/index.ts", "src/templates/assets/javascripts/integrations/search/worker/message/index.ts", "src/templates/assets/javascripts/integrations/search/worker/_/index.ts", "src/templates/assets/javascripts/integrations/version/findurl/index.ts", "src/templates/assets/javascripts/integrations/version/index.ts", "src/templates/assets/javascripts/components/search/query/index.ts", "src/templates/assets/javascripts/components/search/result/index.ts", "src/templates/assets/javascripts/components/search/share/index.ts", "src/templates/assets/javascripts/components/search/suggest/index.ts", "src/templates/assets/javascripts/components/search/_/index.ts", "src/templates/assets/javascripts/components/search/highlight/index.ts", "src/templates/assets/javascripts/components/sidebar/index.ts", "src/templates/assets/javascripts/components/source/facts/github/index.ts", "src/templates/assets/javascripts/components/source/facts/gitlab/index.ts", "src/templates/assets/javascripts/components/source/facts/_/index.ts", "src/templates/assets/javascripts/components/source/_/index.ts", "src/templates/assets/javascripts/components/tabs/index.ts", "src/templates/assets/javascripts/components/toc/index.ts", "src/templates/assets/javascripts/components/top/index.ts", "src/templates/assets/javascripts/patches/ellipsis/index.ts", "src/templates/assets/javascripts/patches/indeterminate/index.ts", "src/templates/assets/javascripts/patches/scrollfix/index.ts", "src/templates/assets/javascripts/patches/scrolllock/index.ts", "src/templates/assets/javascripts/polyfills/index.ts"], + "sourcesContent": ["(function (global, factory) {\n typeof exports === 'object' && typeof module !== 'undefined' ? factory() :\n typeof define === 'function' && define.amd ? define(factory) :\n (factory());\n}(this, (function () { 'use strict';\n\n /**\n * Applies the :focus-visible polyfill at the given scope.\n * A scope in this case is either the top-level Document or a Shadow Root.\n *\n * @param {(Document|ShadowRoot)} scope\n * @see https://github.com/WICG/focus-visible\n */\n function applyFocusVisiblePolyfill(scope) {\n var hadKeyboardEvent = true;\n var hadFocusVisibleRecently = false;\n var hadFocusVisibleRecentlyTimeout = null;\n\n var inputTypesAllowlist = {\n text: true,\n search: true,\n url: true,\n tel: true,\n email: true,\n password: true,\n number: true,\n date: true,\n month: true,\n week: true,\n time: true,\n datetime: true,\n 'datetime-local': true\n };\n\n /**\n * Helper function for legacy browsers and iframes which sometimes focus\n * elements like document, body, and non-interactive SVG.\n * @param {Element} el\n */\n function isValidFocusTarget(el) {\n if (\n el &&\n el !== document &&\n el.nodeName !== 'HTML' &&\n el.nodeName !== 'BODY' &&\n 'classList' in el &&\n 'contains' in el.classList\n ) {\n return true;\n }\n return false;\n }\n\n /**\n * Computes whether the given element should automatically trigger the\n * `focus-visible` class being added, i.e. whether it should always match\n * `:focus-visible` when focused.\n * @param {Element} el\n * @return {boolean}\n */\n function focusTriggersKeyboardModality(el) {\n var type = el.type;\n var tagName = el.tagName;\n\n if (tagName === 'INPUT' && inputTypesAllowlist[type] && !el.readOnly) {\n return true;\n }\n\n if (tagName === 'TEXTAREA' && !el.readOnly) {\n return true;\n }\n\n if (el.isContentEditable) {\n return true;\n }\n\n return false;\n }\n\n /**\n * Add the `focus-visible` class to the given element if it was not added by\n * the author.\n * @param {Element} el\n */\n function addFocusVisibleClass(el) {\n if (el.classList.contains('focus-visible')) {\n return;\n }\n el.classList.add('focus-visible');\n el.setAttribute('data-focus-visible-added', '');\n }\n\n /**\n * Remove the `focus-visible` class from the given element if it was not\n * originally added by the author.\n * @param {Element} el\n */\n function removeFocusVisibleClass(el) {\n if (!el.hasAttribute('data-focus-visible-added')) {\n return;\n }\n el.classList.remove('focus-visible');\n el.removeAttribute('data-focus-visible-added');\n }\n\n /**\n * If the most recent user interaction was via the keyboard;\n * and the key press did not include a meta, alt/option, or control key;\n * then the modality is keyboard. Otherwise, the modality is not keyboard.\n * Apply `focus-visible` to any current active element and keep track\n * of our keyboard modality state with `hadKeyboardEvent`.\n * @param {KeyboardEvent} e\n */\n function onKeyDown(e) {\n if (e.metaKey || e.altKey || e.ctrlKey) {\n return;\n }\n\n if (isValidFocusTarget(scope.activeElement)) {\n addFocusVisibleClass(scope.activeElement);\n }\n\n hadKeyboardEvent = true;\n }\n\n /**\n * If at any point a user clicks with a pointing device, ensure that we change\n * the modality away from keyboard.\n * This avoids the situation where a user presses a key on an already focused\n * element, and then clicks on a different element, focusing it with a\n * pointing device, while we still think we're in keyboard modality.\n * @param {Event} e\n */\n function onPointerDown(e) {\n hadKeyboardEvent = false;\n }\n\n /**\n * On `focus`, add the `focus-visible` class to the target if:\n * - the target received focus as a result of keyboard navigation, or\n * - the event target is an element that will likely require interaction\n * via the keyboard (e.g. a text box)\n * @param {Event} e\n */\n function onFocus(e) {\n // Prevent IE from focusing the document or HTML element.\n if (!isValidFocusTarget(e.target)) {\n return;\n }\n\n if (hadKeyboardEvent || focusTriggersKeyboardModality(e.target)) {\n addFocusVisibleClass(e.target);\n }\n }\n\n /**\n * On `blur`, remove the `focus-visible` class from the target.\n * @param {Event} e\n */\n function onBlur(e) {\n if (!isValidFocusTarget(e.target)) {\n return;\n }\n\n if (\n e.target.classList.contains('focus-visible') ||\n e.target.hasAttribute('data-focus-visible-added')\n ) {\n // To detect a tab/window switch, we look for a blur event followed\n // rapidly by a visibility change.\n // If we don't see a visibility change within 100ms, it's probably a\n // regular focus change.\n hadFocusVisibleRecently = true;\n window.clearTimeout(hadFocusVisibleRecentlyTimeout);\n hadFocusVisibleRecentlyTimeout = window.setTimeout(function() {\n hadFocusVisibleRecently = false;\n }, 100);\n removeFocusVisibleClass(e.target);\n }\n }\n\n /**\n * If the user changes tabs, keep track of whether or not the previously\n * focused element had .focus-visible.\n * @param {Event} e\n */\n function onVisibilityChange(e) {\n if (document.visibilityState === 'hidden') {\n // If the tab becomes active again, the browser will handle calling focus\n // on the element (Safari actually calls it twice).\n // If this tab change caused a blur on an element with focus-visible,\n // re-apply the class when the user switches back to the tab.\n if (hadFocusVisibleRecently) {\n hadKeyboardEvent = true;\n }\n addInitialPointerMoveListeners();\n }\n }\n\n /**\n * Add a group of listeners to detect usage of any pointing devices.\n * These listeners will be added when the polyfill first loads, and anytime\n * the window is blurred, so that they are active when the window regains\n * focus.\n */\n function addInitialPointerMoveListeners() {\n document.addEventListener('mousemove', onInitialPointerMove);\n document.addEventListener('mousedown', onInitialPointerMove);\n document.addEventListener('mouseup', onInitialPointerMove);\n document.addEventListener('pointermove', onInitialPointerMove);\n document.addEventListener('pointerdown', onInitialPointerMove);\n document.addEventListener('pointerup', onInitialPointerMove);\n document.addEventListener('touchmove', onInitialPointerMove);\n document.addEventListener('touchstart', onInitialPointerMove);\n document.addEventListener('touchend', onInitialPointerMove);\n }\n\n function removeInitialPointerMoveListeners() {\n document.removeEventListener('mousemove', onInitialPointerMove);\n document.removeEventListener('mousedown', onInitialPointerMove);\n document.removeEventListener('mouseup', onInitialPointerMove);\n document.removeEventListener('pointermove', onInitialPointerMove);\n document.removeEventListener('pointerdown', onInitialPointerMove);\n document.removeEventListener('pointerup', onInitialPointerMove);\n document.removeEventListener('touchmove', onInitialPointerMove);\n document.removeEventListener('touchstart', onInitialPointerMove);\n document.removeEventListener('touchend', onInitialPointerMove);\n }\n\n /**\n * When the polfyill first loads, assume the user is in keyboard modality.\n * If any event is received from a pointing device (e.g. mouse, pointer,\n * touch), turn off keyboard modality.\n * This accounts for situations where focus enters the page from the URL bar.\n * @param {Event} e\n */\n function onInitialPointerMove(e) {\n // Work around a Safari quirk that fires a mousemove on whenever the\n // window blurs, even if you're tabbing out of the page. \u00AF\\_(\u30C4)_/\u00AF\n if (e.target.nodeName && e.target.nodeName.toLowerCase() === 'html') {\n return;\n }\n\n hadKeyboardEvent = false;\n removeInitialPointerMoveListeners();\n }\n\n // For some kinds of state, we are interested in changes at the global scope\n // only. For example, global pointer input, global key presses and global\n // visibility change should affect the state at every scope:\n document.addEventListener('keydown', onKeyDown, true);\n document.addEventListener('mousedown', onPointerDown, true);\n document.addEventListener('pointerdown', onPointerDown, true);\n document.addEventListener('touchstart', onPointerDown, true);\n document.addEventListener('visibilitychange', onVisibilityChange, true);\n\n addInitialPointerMoveListeners();\n\n // For focus and blur, we specifically care about state changes in the local\n // scope. This is because focus / blur events that originate from within a\n // shadow root are not re-dispatched from the host element if it was already\n // the active element in its own scope:\n scope.addEventListener('focus', onFocus, true);\n scope.addEventListener('blur', onBlur, true);\n\n // We detect that a node is a ShadowRoot by ensuring that it is a\n // DocumentFragment and also has a host property. This check covers native\n // implementation and polyfill implementation transparently. If we only cared\n // about the native implementation, we could just check if the scope was\n // an instance of a ShadowRoot.\n if (scope.nodeType === Node.DOCUMENT_FRAGMENT_NODE && scope.host) {\n // Since a ShadowRoot is a special kind of DocumentFragment, it does not\n // have a root element to add a class to. So, we add this attribute to the\n // host element instead:\n scope.host.setAttribute('data-js-focus-visible', '');\n } else if (scope.nodeType === Node.DOCUMENT_NODE) {\n document.documentElement.classList.add('js-focus-visible');\n document.documentElement.setAttribute('data-js-focus-visible', '');\n }\n }\n\n // It is important to wrap all references to global window and document in\n // these checks to support server-side rendering use cases\n // @see https://github.com/WICG/focus-visible/issues/199\n if (typeof window !== 'undefined' && typeof document !== 'undefined') {\n // Make the polyfill helper globally available. This can be used as a signal\n // to interested libraries that wish to coordinate with the polyfill for e.g.,\n // applying the polyfill to a shadow root:\n window.applyFocusVisiblePolyfill = applyFocusVisiblePolyfill;\n\n // Notify interested libraries of the polyfill's presence, in case the\n // polyfill was loaded lazily:\n var event;\n\n try {\n event = new CustomEvent('focus-visible-polyfill-ready');\n } catch (error) {\n // IE11 does not support using CustomEvent as a constructor directly:\n event = document.createEvent('CustomEvent');\n event.initCustomEvent('focus-visible-polyfill-ready', false, false, {});\n }\n\n window.dispatchEvent(event);\n }\n\n if (typeof document !== 'undefined') {\n // Apply the polyfill to the global document, so that no JavaScript\n // coordination is required to use the polyfill in the top-level document:\n applyFocusVisiblePolyfill(document);\n }\n\n})));\n", "/*!\n * escape-html\n * Copyright(c) 2012-2013 TJ Holowaychuk\n * Copyright(c) 2015 Andreas Lubbe\n * Copyright(c) 2015 Tiancheng \"Timothy\" Gu\n * MIT Licensed\n */\n\n'use strict';\n\n/**\n * Module variables.\n * @private\n */\n\nvar matchHtmlRegExp = /[\"'&<>]/;\n\n/**\n * Module exports.\n * @public\n */\n\nmodule.exports = escapeHtml;\n\n/**\n * Escape special characters in the given string of html.\n *\n * @param {string} string The string to escape for inserting into HTML\n * @return {string}\n * @public\n */\n\nfunction escapeHtml(string) {\n var str = '' + string;\n var match = matchHtmlRegExp.exec(str);\n\n if (!match) {\n return str;\n }\n\n var escape;\n var html = '';\n var index = 0;\n var lastIndex = 0;\n\n for (index = match.index; index < str.length; index++) {\n switch (str.charCodeAt(index)) {\n case 34: // \"\n escape = '"';\n break;\n case 38: // &\n escape = '&';\n break;\n case 39: // '\n escape = ''';\n break;\n case 60: // <\n escape = '<';\n break;\n case 62: // >\n escape = '>';\n break;\n default:\n continue;\n }\n\n if (lastIndex !== index) {\n html += str.substring(lastIndex, index);\n }\n\n lastIndex = index + 1;\n html += escape;\n }\n\n return lastIndex !== index\n ? html + str.substring(lastIndex, index)\n : html;\n}\n", "/*!\n * clipboard.js v2.0.11\n * https://clipboardjs.com/\n *\n * Licensed MIT \u00A9 Zeno Rocha\n */\n(function webpackUniversalModuleDefinition(root, factory) {\n\tif(typeof exports === 'object' && typeof module === 'object')\n\t\tmodule.exports = factory();\n\telse if(typeof define === 'function' && define.amd)\n\t\tdefine([], factory);\n\telse if(typeof exports === 'object')\n\t\texports[\"ClipboardJS\"] = factory();\n\telse\n\t\troot[\"ClipboardJS\"] = factory();\n})(this, function() {\nreturn /******/ (function() { // webpackBootstrap\n/******/ \tvar __webpack_modules__ = ({\n\n/***/ 686:\n/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {\n\n\"use strict\";\n\n// EXPORTS\n__webpack_require__.d(__webpack_exports__, {\n \"default\": function() { return /* binding */ clipboard; }\n});\n\n// EXTERNAL MODULE: ./node_modules/tiny-emitter/index.js\nvar tiny_emitter = __webpack_require__(279);\nvar tiny_emitter_default = /*#__PURE__*/__webpack_require__.n(tiny_emitter);\n// EXTERNAL MODULE: ./node_modules/good-listener/src/listen.js\nvar listen = __webpack_require__(370);\nvar listen_default = /*#__PURE__*/__webpack_require__.n(listen);\n// EXTERNAL MODULE: ./node_modules/select/src/select.js\nvar src_select = __webpack_require__(817);\nvar select_default = /*#__PURE__*/__webpack_require__.n(src_select);\n;// CONCATENATED MODULE: ./src/common/command.js\n/**\n * Executes a given operation type.\n * @param {String} type\n * @return {Boolean}\n */\nfunction command(type) {\n try {\n return document.execCommand(type);\n } catch (err) {\n return false;\n }\n}\n;// CONCATENATED MODULE: ./src/actions/cut.js\n\n\n/**\n * Cut action wrapper.\n * @param {String|HTMLElement} target\n * @return {String}\n */\n\nvar ClipboardActionCut = function ClipboardActionCut(target) {\n var selectedText = select_default()(target);\n command('cut');\n return selectedText;\n};\n\n/* harmony default export */ var actions_cut = (ClipboardActionCut);\n;// CONCATENATED MODULE: ./src/common/create-fake-element.js\n/**\n * Creates a fake textarea element with a value.\n * @param {String} value\n * @return {HTMLElement}\n */\nfunction createFakeElement(value) {\n var isRTL = document.documentElement.getAttribute('dir') === 'rtl';\n var fakeElement = document.createElement('textarea'); // Prevent zooming on iOS\n\n fakeElement.style.fontSize = '12pt'; // Reset box model\n\n fakeElement.style.border = '0';\n fakeElement.style.padding = '0';\n fakeElement.style.margin = '0'; // Move element out of screen horizontally\n\n fakeElement.style.position = 'absolute';\n fakeElement.style[isRTL ? 'right' : 'left'] = '-9999px'; // Move element to the same position vertically\n\n var yPosition = window.pageYOffset || document.documentElement.scrollTop;\n fakeElement.style.top = \"\".concat(yPosition, \"px\");\n fakeElement.setAttribute('readonly', '');\n fakeElement.value = value;\n return fakeElement;\n}\n;// CONCATENATED MODULE: ./src/actions/copy.js\n\n\n\n/**\n * Create fake copy action wrapper using a fake element.\n * @param {String} target\n * @param {Object} options\n * @return {String}\n */\n\nvar fakeCopyAction = function fakeCopyAction(value, options) {\n var fakeElement = createFakeElement(value);\n options.container.appendChild(fakeElement);\n var selectedText = select_default()(fakeElement);\n command('copy');\n fakeElement.remove();\n return selectedText;\n};\n/**\n * Copy action wrapper.\n * @param {String|HTMLElement} target\n * @param {Object} options\n * @return {String}\n */\n\n\nvar ClipboardActionCopy = function ClipboardActionCopy(target) {\n var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {\n container: document.body\n };\n var selectedText = '';\n\n if (typeof target === 'string') {\n selectedText = fakeCopyAction(target, options);\n } else if (target instanceof HTMLInputElement && !['text', 'search', 'url', 'tel', 'password'].includes(target === null || target === void 0 ? void 0 : target.type)) {\n // If input type doesn't support `setSelectionRange`. Simulate it. https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement/setSelectionRange\n selectedText = fakeCopyAction(target.value, options);\n } else {\n selectedText = select_default()(target);\n command('copy');\n }\n\n return selectedText;\n};\n\n/* harmony default export */ var actions_copy = (ClipboardActionCopy);\n;// CONCATENATED MODULE: ./src/actions/default.js\nfunction _typeof(obj) { \"@babel/helpers - typeof\"; if (typeof Symbol === \"function\" && typeof Symbol.iterator === \"symbol\") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === \"function\" && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj; }; } return _typeof(obj); }\n\n\n\n/**\n * Inner function which performs selection from either `text` or `target`\n * properties and then executes copy or cut operations.\n * @param {Object} options\n */\n\nvar ClipboardActionDefault = function ClipboardActionDefault() {\n var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n // Defines base properties passed from constructor.\n var _options$action = options.action,\n action = _options$action === void 0 ? 'copy' : _options$action,\n container = options.container,\n target = options.target,\n text = options.text; // Sets the `action` to be performed which can be either 'copy' or 'cut'.\n\n if (action !== 'copy' && action !== 'cut') {\n throw new Error('Invalid \"action\" value, use either \"copy\" or \"cut\"');\n } // Sets the `target` property using an element that will be have its content copied.\n\n\n if (target !== undefined) {\n if (target && _typeof(target) === 'object' && target.nodeType === 1) {\n if (action === 'copy' && target.hasAttribute('disabled')) {\n throw new Error('Invalid \"target\" attribute. Please use \"readonly\" instead of \"disabled\" attribute');\n }\n\n if (action === 'cut' && (target.hasAttribute('readonly') || target.hasAttribute('disabled'))) {\n throw new Error('Invalid \"target\" attribute. You can\\'t cut text from elements with \"readonly\" or \"disabled\" attributes');\n }\n } else {\n throw new Error('Invalid \"target\" value, use a valid Element');\n }\n } // Define selection strategy based on `text` property.\n\n\n if (text) {\n return actions_copy(text, {\n container: container\n });\n } // Defines which selection strategy based on `target` property.\n\n\n if (target) {\n return action === 'cut' ? actions_cut(target) : actions_copy(target, {\n container: container\n });\n }\n};\n\n/* harmony default export */ var actions_default = (ClipboardActionDefault);\n;// CONCATENATED MODULE: ./src/clipboard.js\nfunction clipboard_typeof(obj) { \"@babel/helpers - typeof\"; if (typeof Symbol === \"function\" && typeof Symbol.iterator === \"symbol\") { clipboard_typeof = function _typeof(obj) { return typeof obj; }; } else { clipboard_typeof = function _typeof(obj) { return obj && typeof Symbol === \"function\" && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj; }; } return clipboard_typeof(obj); }\n\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\n\nfunction _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }\n\nfunction _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }\n\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function\"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); }\n\nfunction _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }\n\nfunction _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; }\n\nfunction _possibleConstructorReturn(self, call) { if (call && (clipboard_typeof(call) === \"object\" || typeof call === \"function\")) { return call; } return _assertThisInitialized(self); }\n\nfunction _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return self; }\n\nfunction _isNativeReflectConstruct() { if (typeof Reflect === \"undefined\" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === \"function\") return true; try { Date.prototype.toString.call(Reflect.construct(Date, [], function () {})); return true; } catch (e) { return false; } }\n\nfunction _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }\n\n\n\n\n\n\n/**\n * Helper function to retrieve attribute value.\n * @param {String} suffix\n * @param {Element} element\n */\n\nfunction getAttributeValue(suffix, element) {\n var attribute = \"data-clipboard-\".concat(suffix);\n\n if (!element.hasAttribute(attribute)) {\n return;\n }\n\n return element.getAttribute(attribute);\n}\n/**\n * Base class which takes one or more elements, adds event listeners to them,\n * and instantiates a new `ClipboardAction` on each click.\n */\n\n\nvar Clipboard = /*#__PURE__*/function (_Emitter) {\n _inherits(Clipboard, _Emitter);\n\n var _super = _createSuper(Clipboard);\n\n /**\n * @param {String|HTMLElement|HTMLCollection|NodeList} trigger\n * @param {Object} options\n */\n function Clipboard(trigger, options) {\n var _this;\n\n _classCallCheck(this, Clipboard);\n\n _this = _super.call(this);\n\n _this.resolveOptions(options);\n\n _this.listenClick(trigger);\n\n return _this;\n }\n /**\n * Defines if attributes would be resolved using internal setter functions\n * or custom functions that were passed in the constructor.\n * @param {Object} options\n */\n\n\n _createClass(Clipboard, [{\n key: \"resolveOptions\",\n value: function resolveOptions() {\n var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};\n this.action = typeof options.action === 'function' ? options.action : this.defaultAction;\n this.target = typeof options.target === 'function' ? options.target : this.defaultTarget;\n this.text = typeof options.text === 'function' ? options.text : this.defaultText;\n this.container = clipboard_typeof(options.container) === 'object' ? options.container : document.body;\n }\n /**\n * Adds a click event listener to the passed trigger.\n * @param {String|HTMLElement|HTMLCollection|NodeList} trigger\n */\n\n }, {\n key: \"listenClick\",\n value: function listenClick(trigger) {\n var _this2 = this;\n\n this.listener = listen_default()(trigger, 'click', function (e) {\n return _this2.onClick(e);\n });\n }\n /**\n * Defines a new `ClipboardAction` on each click event.\n * @param {Event} e\n */\n\n }, {\n key: \"onClick\",\n value: function onClick(e) {\n var trigger = e.delegateTarget || e.currentTarget;\n var action = this.action(trigger) || 'copy';\n var text = actions_default({\n action: action,\n container: this.container,\n target: this.target(trigger),\n text: this.text(trigger)\n }); // Fires an event based on the copy operation result.\n\n this.emit(text ? 'success' : 'error', {\n action: action,\n text: text,\n trigger: trigger,\n clearSelection: function clearSelection() {\n if (trigger) {\n trigger.focus();\n }\n\n window.getSelection().removeAllRanges();\n }\n });\n }\n /**\n * Default `action` lookup function.\n * @param {Element} trigger\n */\n\n }, {\n key: \"defaultAction\",\n value: function defaultAction(trigger) {\n return getAttributeValue('action', trigger);\n }\n /**\n * Default `target` lookup function.\n * @param {Element} trigger\n */\n\n }, {\n key: \"defaultTarget\",\n value: function defaultTarget(trigger) {\n var selector = getAttributeValue('target', trigger);\n\n if (selector) {\n return document.querySelector(selector);\n }\n }\n /**\n * Allow fire programmatically a copy action\n * @param {String|HTMLElement} target\n * @param {Object} options\n * @returns Text copied.\n */\n\n }, {\n key: \"defaultText\",\n\n /**\n * Default `text` lookup function.\n * @param {Element} trigger\n */\n value: function defaultText(trigger) {\n return getAttributeValue('text', trigger);\n }\n /**\n * Destroy lifecycle.\n */\n\n }, {\n key: \"destroy\",\n value: function destroy() {\n this.listener.destroy();\n }\n }], [{\n key: \"copy\",\n value: function copy(target) {\n var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {\n container: document.body\n };\n return actions_copy(target, options);\n }\n /**\n * Allow fire programmatically a cut action\n * @param {String|HTMLElement} target\n * @returns Text cutted.\n */\n\n }, {\n key: \"cut\",\n value: function cut(target) {\n return actions_cut(target);\n }\n /**\n * Returns the support of the given action, or all actions if no action is\n * given.\n * @param {String} [action]\n */\n\n }, {\n key: \"isSupported\",\n value: function isSupported() {\n var action = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : ['copy', 'cut'];\n var actions = typeof action === 'string' ? [action] : action;\n var support = !!document.queryCommandSupported;\n actions.forEach(function (action) {\n support = support && !!document.queryCommandSupported(action);\n });\n return support;\n }\n }]);\n\n return Clipboard;\n}((tiny_emitter_default()));\n\n/* harmony default export */ var clipboard = (Clipboard);\n\n/***/ }),\n\n/***/ 828:\n/***/ (function(module) {\n\nvar DOCUMENT_NODE_TYPE = 9;\n\n/**\n * A polyfill for Element.matches()\n */\nif (typeof Element !== 'undefined' && !Element.prototype.matches) {\n var proto = Element.prototype;\n\n proto.matches = proto.matchesSelector ||\n proto.mozMatchesSelector ||\n proto.msMatchesSelector ||\n proto.oMatchesSelector ||\n proto.webkitMatchesSelector;\n}\n\n/**\n * Finds the closest parent that matches a selector.\n *\n * @param {Element} element\n * @param {String} selector\n * @return {Function}\n */\nfunction closest (element, selector) {\n while (element && element.nodeType !== DOCUMENT_NODE_TYPE) {\n if (typeof element.matches === 'function' &&\n element.matches(selector)) {\n return element;\n }\n element = element.parentNode;\n }\n}\n\nmodule.exports = closest;\n\n\n/***/ }),\n\n/***/ 438:\n/***/ (function(module, __unused_webpack_exports, __webpack_require__) {\n\nvar closest = __webpack_require__(828);\n\n/**\n * Delegates event to a selector.\n *\n * @param {Element} element\n * @param {String} selector\n * @param {String} type\n * @param {Function} callback\n * @param {Boolean} useCapture\n * @return {Object}\n */\nfunction _delegate(element, selector, type, callback, useCapture) {\n var listenerFn = listener.apply(this, arguments);\n\n element.addEventListener(type, listenerFn, useCapture);\n\n return {\n destroy: function() {\n element.removeEventListener(type, listenerFn, useCapture);\n }\n }\n}\n\n/**\n * Delegates event to a selector.\n *\n * @param {Element|String|Array} [elements]\n * @param {String} selector\n * @param {String} type\n * @param {Function} callback\n * @param {Boolean} useCapture\n * @return {Object}\n */\nfunction delegate(elements, selector, type, callback, useCapture) {\n // Handle the regular Element usage\n if (typeof elements.addEventListener === 'function') {\n return _delegate.apply(null, arguments);\n }\n\n // Handle Element-less usage, it defaults to global delegation\n if (typeof type === 'function') {\n // Use `document` as the first parameter, then apply arguments\n // This is a short way to .unshift `arguments` without running into deoptimizations\n return _delegate.bind(null, document).apply(null, arguments);\n }\n\n // Handle Selector-based usage\n if (typeof elements === 'string') {\n elements = document.querySelectorAll(elements);\n }\n\n // Handle Array-like based usage\n return Array.prototype.map.call(elements, function (element) {\n return _delegate(element, selector, type, callback, useCapture);\n });\n}\n\n/**\n * Finds closest match and invokes callback.\n *\n * @param {Element} element\n * @param {String} selector\n * @param {String} type\n * @param {Function} callback\n * @return {Function}\n */\nfunction listener(element, selector, type, callback) {\n return function(e) {\n e.delegateTarget = closest(e.target, selector);\n\n if (e.delegateTarget) {\n callback.call(element, e);\n }\n }\n}\n\nmodule.exports = delegate;\n\n\n/***/ }),\n\n/***/ 879:\n/***/ (function(__unused_webpack_module, exports) {\n\n/**\n * Check if argument is a HTML element.\n *\n * @param {Object} value\n * @return {Boolean}\n */\nexports.node = function(value) {\n return value !== undefined\n && value instanceof HTMLElement\n && value.nodeType === 1;\n};\n\n/**\n * Check if argument is a list of HTML elements.\n *\n * @param {Object} value\n * @return {Boolean}\n */\nexports.nodeList = function(value) {\n var type = Object.prototype.toString.call(value);\n\n return value !== undefined\n && (type === '[object NodeList]' || type === '[object HTMLCollection]')\n && ('length' in value)\n && (value.length === 0 || exports.node(value[0]));\n};\n\n/**\n * Check if argument is a string.\n *\n * @param {Object} value\n * @return {Boolean}\n */\nexports.string = function(value) {\n return typeof value === 'string'\n || value instanceof String;\n};\n\n/**\n * Check if argument is a function.\n *\n * @param {Object} value\n * @return {Boolean}\n */\nexports.fn = function(value) {\n var type = Object.prototype.toString.call(value);\n\n return type === '[object Function]';\n};\n\n\n/***/ }),\n\n/***/ 370:\n/***/ (function(module, __unused_webpack_exports, __webpack_require__) {\n\nvar is = __webpack_require__(879);\nvar delegate = __webpack_require__(438);\n\n/**\n * Validates all params and calls the right\n * listener function based on its target type.\n *\n * @param {String|HTMLElement|HTMLCollection|NodeList} target\n * @param {String} type\n * @param {Function} callback\n * @return {Object}\n */\nfunction listen(target, type, callback) {\n if (!target && !type && !callback) {\n throw new Error('Missing required arguments');\n }\n\n if (!is.string(type)) {\n throw new TypeError('Second argument must be a String');\n }\n\n if (!is.fn(callback)) {\n throw new TypeError('Third argument must be a Function');\n }\n\n if (is.node(target)) {\n return listenNode(target, type, callback);\n }\n else if (is.nodeList(target)) {\n return listenNodeList(target, type, callback);\n }\n else if (is.string(target)) {\n return listenSelector(target, type, callback);\n }\n else {\n throw new TypeError('First argument must be a String, HTMLElement, HTMLCollection, or NodeList');\n }\n}\n\n/**\n * Adds an event listener to a HTML element\n * and returns a remove listener function.\n *\n * @param {HTMLElement} node\n * @param {String} type\n * @param {Function} callback\n * @return {Object}\n */\nfunction listenNode(node, type, callback) {\n node.addEventListener(type, callback);\n\n return {\n destroy: function() {\n node.removeEventListener(type, callback);\n }\n }\n}\n\n/**\n * Add an event listener to a list of HTML elements\n * and returns a remove listener function.\n *\n * @param {NodeList|HTMLCollection} nodeList\n * @param {String} type\n * @param {Function} callback\n * @return {Object}\n */\nfunction listenNodeList(nodeList, type, callback) {\n Array.prototype.forEach.call(nodeList, function(node) {\n node.addEventListener(type, callback);\n });\n\n return {\n destroy: function() {\n Array.prototype.forEach.call(nodeList, function(node) {\n node.removeEventListener(type, callback);\n });\n }\n }\n}\n\n/**\n * Add an event listener to a selector\n * and returns a remove listener function.\n *\n * @param {String} selector\n * @param {String} type\n * @param {Function} callback\n * @return {Object}\n */\nfunction listenSelector(selector, type, callback) {\n return delegate(document.body, selector, type, callback);\n}\n\nmodule.exports = listen;\n\n\n/***/ }),\n\n/***/ 817:\n/***/ (function(module) {\n\nfunction select(element) {\n var selectedText;\n\n if (element.nodeName === 'SELECT') {\n element.focus();\n\n selectedText = element.value;\n }\n else if (element.nodeName === 'INPUT' || element.nodeName === 'TEXTAREA') {\n var isReadOnly = element.hasAttribute('readonly');\n\n if (!isReadOnly) {\n element.setAttribute('readonly', '');\n }\n\n element.select();\n element.setSelectionRange(0, element.value.length);\n\n if (!isReadOnly) {\n element.removeAttribute('readonly');\n }\n\n selectedText = element.value;\n }\n else {\n if (element.hasAttribute('contenteditable')) {\n element.focus();\n }\n\n var selection = window.getSelection();\n var range = document.createRange();\n\n range.selectNodeContents(element);\n selection.removeAllRanges();\n selection.addRange(range);\n\n selectedText = selection.toString();\n }\n\n return selectedText;\n}\n\nmodule.exports = select;\n\n\n/***/ }),\n\n/***/ 279:\n/***/ (function(module) {\n\nfunction E () {\n // Keep this empty so it's easier to inherit from\n // (via https://github.com/lipsmack from https://github.com/scottcorgan/tiny-emitter/issues/3)\n}\n\nE.prototype = {\n on: function (name, callback, ctx) {\n var e = this.e || (this.e = {});\n\n (e[name] || (e[name] = [])).push({\n fn: callback,\n ctx: ctx\n });\n\n return this;\n },\n\n once: function (name, callback, ctx) {\n var self = this;\n function listener () {\n self.off(name, listener);\n callback.apply(ctx, arguments);\n };\n\n listener._ = callback\n return this.on(name, listener, ctx);\n },\n\n emit: function (name) {\n var data = [].slice.call(arguments, 1);\n var evtArr = ((this.e || (this.e = {}))[name] || []).slice();\n var i = 0;\n var len = evtArr.length;\n\n for (i; i < len; i++) {\n evtArr[i].fn.apply(evtArr[i].ctx, data);\n }\n\n return this;\n },\n\n off: function (name, callback) {\n var e = this.e || (this.e = {});\n var evts = e[name];\n var liveEvents = [];\n\n if (evts && callback) {\n for (var i = 0, len = evts.length; i < len; i++) {\n if (evts[i].fn !== callback && evts[i].fn._ !== callback)\n liveEvents.push(evts[i]);\n }\n }\n\n // Remove event from queue to prevent memory leak\n // Suggested by https://github.com/lazd\n // Ref: https://github.com/scottcorgan/tiny-emitter/commit/c6ebfaa9bc973b33d110a84a307742b7cf94c953#commitcomment-5024910\n\n (liveEvents.length)\n ? e[name] = liveEvents\n : delete e[name];\n\n return this;\n }\n};\n\nmodule.exports = E;\nmodule.exports.TinyEmitter = E;\n\n\n/***/ })\n\n/******/ \t});\n/************************************************************************/\n/******/ \t// The module cache\n/******/ \tvar __webpack_module_cache__ = {};\n/******/ \t\n/******/ \t// The require function\n/******/ \tfunction __webpack_require__(moduleId) {\n/******/ \t\t// Check if module is in cache\n/******/ \t\tif(__webpack_module_cache__[moduleId]) {\n/******/ \t\t\treturn __webpack_module_cache__[moduleId].exports;\n/******/ \t\t}\n/******/ \t\t// Create a new module (and put it into the cache)\n/******/ \t\tvar module = __webpack_module_cache__[moduleId] = {\n/******/ \t\t\t// no module.id needed\n/******/ \t\t\t// no module.loaded needed\n/******/ \t\t\texports: {}\n/******/ \t\t};\n/******/ \t\n/******/ \t\t// Execute the module function\n/******/ \t\t__webpack_modules__[moduleId](module, module.exports, __webpack_require__);\n/******/ \t\n/******/ \t\t// Return the exports of the module\n/******/ \t\treturn module.exports;\n/******/ \t}\n/******/ \t\n/************************************************************************/\n/******/ \t/* webpack/runtime/compat get default export */\n/******/ \t!function() {\n/******/ \t\t// getDefaultExport function for compatibility with non-harmony modules\n/******/ \t\t__webpack_require__.n = function(module) {\n/******/ \t\t\tvar getter = module && module.__esModule ?\n/******/ \t\t\t\tfunction() { return module['default']; } :\n/******/ \t\t\t\tfunction() { return module; };\n/******/ \t\t\t__webpack_require__.d(getter, { a: getter });\n/******/ \t\t\treturn getter;\n/******/ \t\t};\n/******/ \t}();\n/******/ \t\n/******/ \t/* webpack/runtime/define property getters */\n/******/ \t!function() {\n/******/ \t\t// define getter functions for harmony exports\n/******/ \t\t__webpack_require__.d = function(exports, definition) {\n/******/ \t\t\tfor(var key in definition) {\n/******/ \t\t\t\tif(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n/******/ \t\t\t\t\tObject.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n/******/ \t\t\t\t}\n/******/ \t\t\t}\n/******/ \t\t};\n/******/ \t}();\n/******/ \t\n/******/ \t/* webpack/runtime/hasOwnProperty shorthand */\n/******/ \t!function() {\n/******/ \t\t__webpack_require__.o = function(obj, prop) { return Object.prototype.hasOwnProperty.call(obj, prop); }\n/******/ \t}();\n/******/ \t\n/************************************************************************/\n/******/ \t// module exports must be returned from runtime so entry inlining is disabled\n/******/ \t// startup\n/******/ \t// Load entry module and return exports\n/******/ \treturn __webpack_require__(686);\n/******/ })()\n.default;\n});", "/*\n * Copyright (c) 2016-2025 Martin Donath \n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\nimport \"focus-visible\"\n\nimport {\n EMPTY,\n NEVER,\n Observable,\n Subject,\n defer,\n delay,\n filter,\n map,\n merge,\n mergeWith,\n shareReplay,\n switchMap\n} from \"rxjs\"\n\nimport { configuration, feature } from \"./_\"\nimport {\n at,\n getActiveElement,\n getOptionalElement,\n requestJSON,\n setLocation,\n setToggle,\n watchDocument,\n watchKeyboard,\n watchLocation,\n watchLocationTarget,\n watchMedia,\n watchPrint,\n watchScript,\n watchViewport\n} from \"./browser\"\nimport {\n getComponentElement,\n getComponentElements,\n mountAnnounce,\n mountBackToTop,\n mountConsent,\n mountContent,\n mountDialog,\n mountHeader,\n mountHeaderTitle,\n mountPalette,\n mountProgress,\n mountSearch,\n mountSearchHiglight,\n mountSidebar,\n mountSource,\n mountTableOfContents,\n mountTabs,\n watchHeader,\n watchMain\n} from \"./components\"\nimport {\n SearchIndex,\n fetchSitemap,\n setupAlternate,\n setupClipboardJS,\n setupInstantNavigation,\n setupVersionSelector\n} from \"./integrations\"\nimport {\n patchEllipsis,\n patchIndeterminate,\n patchScrollfix,\n patchScrolllock\n} from \"./patches\"\nimport \"./polyfills\"\n\n/* ----------------------------------------------------------------------------\n * Functions - @todo refactor\n * ------------------------------------------------------------------------- */\n\n/**\n * Fetch search index\n *\n * @returns Search index observable\n */\nfunction fetchSearchIndex(): Observable {\n if (location.protocol === \"file:\") {\n return watchScript(\n `${new URL(\"search/search_index.js\", config.base)}`\n )\n .pipe(\n // @ts-ignore - @todo fix typings\n map(() => __index),\n shareReplay(1)\n )\n } else {\n return requestJSON(\n new URL(\"search/search_index.json\", config.base)\n )\n }\n}\n\n/* ----------------------------------------------------------------------------\n * Application\n * ------------------------------------------------------------------------- */\n\n/* Yay, JavaScript is available */\ndocument.documentElement.classList.remove(\"no-js\")\ndocument.documentElement.classList.add(\"js\")\n\n/* Set up navigation observables and subjects */\nconst document$ = watchDocument()\nconst location$ = watchLocation()\nconst target$ = watchLocationTarget(location$)\nconst keyboard$ = watchKeyboard()\n\n/* Set up media observables */\nconst viewport$ = watchViewport()\nconst tablet$ = watchMedia(\"(min-width: 60em)\")\nconst screen$ = watchMedia(\"(min-width: 76.25em)\")\nconst print$ = watchPrint()\n\n/* Retrieve search index, if search is enabled */\nconst config = configuration()\nconst index$ = document.forms.namedItem(\"search\")\n ? fetchSearchIndex()\n : NEVER\n\n/* Set up Clipboard.js integration */\nconst alert$ = new Subject()\nsetupClipboardJS({ alert$ })\n\n/* Set up language selector */\nsetupAlternate({ document$ })\n\n/* Set up progress indicator */\nconst progress$ = new Subject()\n\n/* Set up sitemap for instant navigation and previews */\nconst sitemap$ = fetchSitemap(config.base)\n\n/* Set up instant navigation, if enabled */\nif (feature(\"navigation.instant\"))\n setupInstantNavigation({ sitemap$, location$, viewport$, progress$ })\n .subscribe(document$)\n\n/* Set up version selector */\nif (config.version?.provider === \"mike\")\n setupVersionSelector({ document$ })\n\n/* Always close drawer and search on navigation */\nmerge(location$, target$)\n .pipe(\n delay(125)\n )\n .subscribe(() => {\n setToggle(\"drawer\", false)\n setToggle(\"search\", false)\n })\n\n/* Set up global keyboard handlers */\nkeyboard$\n .pipe(\n filter(({ mode }) => mode === \"global\")\n )\n .subscribe(key => {\n switch (key.type) {\n\n /* Go to previous page */\n case \"p\":\n case \",\":\n const prev = getOptionalElement(\"link[rel=prev]\")\n if (typeof prev !== \"undefined\")\n setLocation(prev)\n break\n\n /* Go to next page */\n case \"n\":\n case \".\":\n const next = getOptionalElement(\"link[rel=next]\")\n if (typeof next !== \"undefined\")\n setLocation(next)\n break\n\n /* Expand navigation, see https://bit.ly/3ZjG5io */\n case \"Enter\":\n const active = getActiveElement()\n if (active instanceof HTMLLabelElement)\n active.click()\n }\n })\n\n/* Set up patches */\npatchEllipsis({ viewport$, document$ })\npatchIndeterminate({ document$, tablet$ })\npatchScrollfix({ document$ })\npatchScrolllock({ viewport$, tablet$ })\n\n/* Set up header and main area observable */\nconst header$ = watchHeader(getComponentElement(\"header\"), { viewport$ })\nconst main$ = document$\n .pipe(\n map(() => getComponentElement(\"main\")),\n switchMap(el => watchMain(el, { viewport$, header$ })),\n shareReplay(1)\n )\n\n/* Set up control component observables */\nconst control$ = merge(\n\n /* Consent */\n ...getComponentElements(\"consent\")\n .map(el => mountConsent(el, { target$ })),\n\n /* Dialog */\n ...getComponentElements(\"dialog\")\n .map(el => mountDialog(el, { alert$ })),\n\n /* Color palette */\n ...getComponentElements(\"palette\")\n .map(el => mountPalette(el)),\n\n /* Progress bar */\n ...getComponentElements(\"progress\")\n .map(el => mountProgress(el, { progress$ })),\n\n /* Search */\n ...getComponentElements(\"search\")\n .map(el => mountSearch(el, { index$, keyboard$ })),\n\n /* Repository information */\n ...getComponentElements(\"source\")\n .map(el => mountSource(el))\n)\n\n/* Set up content component observables */\nconst content$ = defer(() => merge(\n\n /* Announcement bar */\n ...getComponentElements(\"announce\")\n .map(el => mountAnnounce(el)),\n\n /* Content */\n ...getComponentElements(\"content\")\n .map(el => mountContent(el, { sitemap$, viewport$, target$, print$ })),\n\n /* Search highlighting */\n ...getComponentElements(\"content\")\n .map(el => feature(\"search.highlight\")\n ? mountSearchHiglight(el, { index$, location$ })\n : EMPTY\n ),\n\n /* Header */\n ...getComponentElements(\"header\")\n .map(el => mountHeader(el, { viewport$, header$, main$ })),\n\n /* Header title */\n ...getComponentElements(\"header-title\")\n .map(el => mountHeaderTitle(el, { viewport$, header$ })),\n\n /* Sidebar */\n ...getComponentElements(\"sidebar\")\n .map(el => el.getAttribute(\"data-md-type\") === \"navigation\"\n ? at(screen$, () => mountSidebar(el, { viewport$, header$, main$ }))\n : at(tablet$, () => mountSidebar(el, { viewport$, header$, main$ }))\n ),\n\n /* Navigation tabs */\n ...getComponentElements(\"tabs\")\n .map(el => mountTabs(el, { viewport$, header$ })),\n\n /* Table of contents */\n ...getComponentElements(\"toc\")\n .map(el => mountTableOfContents(el, {\n viewport$, header$, main$, target$\n })),\n\n /* Back-to-top button */\n ...getComponentElements(\"top\")\n .map(el => mountBackToTop(el, { viewport$, header$, main$, target$ }))\n))\n\n/* Set up component observables */\nconst component$ = document$\n .pipe(\n switchMap(() => content$),\n mergeWith(control$),\n shareReplay(1)\n )\n\n/* Subscribe to all components */\ncomponent$.subscribe()\n\n/* ----------------------------------------------------------------------------\n * Exports\n * ------------------------------------------------------------------------- */\n\nwindow.document$ = document$ /* Document observable */\nwindow.location$ = location$ /* Location subject */\nwindow.target$ = target$ /* Location target observable */\nwindow.keyboard$ = keyboard$ /* Keyboard observable */\nwindow.viewport$ = viewport$ /* Viewport observable */\nwindow.tablet$ = tablet$ /* Media tablet observable */\nwindow.screen$ = screen$ /* Media screen observable */\nwindow.print$ = print$ /* Media print observable */\nwindow.alert$ = alert$ /* Alert subject */\nwindow.progress$ = progress$ /* Progress indicator subject */\nwindow.component$ = component$ /* Component observable */\n", "/******************************************************************************\nCopyright (c) Microsoft Corporation.\n\nPermission to use, copy, modify, and/or distribute this software for any\npurpose with or without fee is hereby granted.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH\nREGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY\nAND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,\nINDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM\nLOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR\nOTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR\nPERFORMANCE OF THIS SOFTWARE.\n***************************************************************************** */\n/* global Reflect, Promise, SuppressedError, Symbol, Iterator */\n\nvar extendStatics = function(d, b) {\n extendStatics = Object.setPrototypeOf ||\n ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\n function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };\n return extendStatics(d, b);\n};\n\nexport function __extends(d, b) {\n if (typeof b !== \"function\" && b !== null)\n throw new TypeError(\"Class extends value \" + String(b) + \" is not a constructor or null\");\n extendStatics(d, b);\n function __() { this.constructor = d; }\n d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\n}\n\nexport var __assign = function() {\n __assign = Object.assign || function __assign(t) {\n for (var s, i = 1, n = arguments.length; i < n; i++) {\n s = arguments[i];\n for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];\n }\n return t;\n }\n return __assign.apply(this, arguments);\n}\n\nexport function __rest(s, e) {\n var t = {};\n for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)\n t[p] = s[p];\n if (s != null && typeof Object.getOwnPropertySymbols === \"function\")\n for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {\n if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))\n t[p[i]] = s[p[i]];\n }\n return t;\n}\n\nexport function __decorate(decorators, target, key, desc) {\n var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;\n if (typeof Reflect === \"object\" && typeof Reflect.decorate === \"function\") r = Reflect.decorate(decorators, target, key, desc);\n else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;\n return c > 3 && r && Object.defineProperty(target, key, r), r;\n}\n\nexport function __param(paramIndex, decorator) {\n return function (target, key) { decorator(target, key, paramIndex); }\n}\n\nexport function __esDecorate(ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {\n function accept(f) { if (f !== void 0 && typeof f !== \"function\") throw new TypeError(\"Function expected\"); return f; }\n var kind = contextIn.kind, key = kind === \"getter\" ? \"get\" : kind === \"setter\" ? \"set\" : \"value\";\n var target = !descriptorIn && ctor ? contextIn[\"static\"] ? ctor : ctor.prototype : null;\n var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});\n var _, done = false;\n for (var i = decorators.length - 1; i >= 0; i--) {\n var context = {};\n for (var p in contextIn) context[p] = p === \"access\" ? {} : contextIn[p];\n for (var p in contextIn.access) context.access[p] = contextIn.access[p];\n context.addInitializer = function (f) { if (done) throw new TypeError(\"Cannot add initializers after decoration has completed\"); extraInitializers.push(accept(f || null)); };\n var result = (0, decorators[i])(kind === \"accessor\" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);\n if (kind === \"accessor\") {\n if (result === void 0) continue;\n if (result === null || typeof result !== \"object\") throw new TypeError(\"Object expected\");\n if (_ = accept(result.get)) descriptor.get = _;\n if (_ = accept(result.set)) descriptor.set = _;\n if (_ = accept(result.init)) initializers.unshift(_);\n }\n else if (_ = accept(result)) {\n if (kind === \"field\") initializers.unshift(_);\n else descriptor[key] = _;\n }\n }\n if (target) Object.defineProperty(target, contextIn.name, descriptor);\n done = true;\n};\n\nexport function __runInitializers(thisArg, initializers, value) {\n var useValue = arguments.length > 2;\n for (var i = 0; i < initializers.length; i++) {\n value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);\n }\n return useValue ? value : void 0;\n};\n\nexport function __propKey(x) {\n return typeof x === \"symbol\" ? x : \"\".concat(x);\n};\n\nexport function __setFunctionName(f, name, prefix) {\n if (typeof name === \"symbol\") name = name.description ? \"[\".concat(name.description, \"]\") : \"\";\n return Object.defineProperty(f, \"name\", { configurable: true, value: prefix ? \"\".concat(prefix, \" \", name) : name });\n};\n\nexport function __metadata(metadataKey, metadataValue) {\n if (typeof Reflect === \"object\" && typeof Reflect.metadata === \"function\") return Reflect.metadata(metadataKey, metadataValue);\n}\n\nexport function __awaiter(thisArg, _arguments, P, generator) {\n function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }\n return new (P || (P = Promise))(function (resolve, reject) {\n function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }\n function rejected(value) { try { step(generator[\"throw\"](value)); } catch (e) { reject(e); } }\n function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }\n step((generator = generator.apply(thisArg, _arguments || [])).next());\n });\n}\n\nexport function __generator(thisArg, body) {\n var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === \"function\" ? Iterator : Object).prototype);\n return g.next = verb(0), g[\"throw\"] = verb(1), g[\"return\"] = verb(2), typeof Symbol === \"function\" && (g[Symbol.iterator] = function() { return this; }), g;\n function verb(n) { return function (v) { return step([n, v]); }; }\n function step(op) {\n if (f) throw new TypeError(\"Generator is already executing.\");\n while (g && (g = 0, op[0] && (_ = 0)), _) try {\n if (f = 1, y && (t = op[0] & 2 ? y[\"return\"] : op[0] ? y[\"throw\"] || ((t = y[\"return\"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;\n if (y = 0, t) op = [op[0] & 2, t.value];\n switch (op[0]) {\n case 0: case 1: t = op; break;\n case 4: _.label++; return { value: op[1], done: false };\n case 5: _.label++; y = op[1]; op = [0]; continue;\n case 7: op = _.ops.pop(); _.trys.pop(); continue;\n default:\n if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }\n if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }\n if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }\n if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }\n if (t[2]) _.ops.pop();\n _.trys.pop(); continue;\n }\n op = body.call(thisArg, _);\n } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }\n if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };\n }\n}\n\nexport var __createBinding = Object.create ? (function(o, m, k, k2) {\n if (k2 === undefined) k2 = k;\n var desc = Object.getOwnPropertyDescriptor(m, k);\n if (!desc || (\"get\" in desc ? !m.__esModule : desc.writable || desc.configurable)) {\n desc = { enumerable: true, get: function() { return m[k]; } };\n }\n Object.defineProperty(o, k2, desc);\n}) : (function(o, m, k, k2) {\n if (k2 === undefined) k2 = k;\n o[k2] = m[k];\n});\n\nexport function __exportStar(m, o) {\n for (var p in m) if (p !== \"default\" && !Object.prototype.hasOwnProperty.call(o, p)) __createBinding(o, m, p);\n}\n\nexport function __values(o) {\n var s = typeof Symbol === \"function\" && Symbol.iterator, m = s && o[s], i = 0;\n if (m) return m.call(o);\n if (o && typeof o.length === \"number\") return {\n next: function () {\n if (o && i >= o.length) o = void 0;\n return { value: o && o[i++], done: !o };\n }\n };\n throw new TypeError(s ? \"Object is not iterable.\" : \"Symbol.iterator is not defined.\");\n}\n\nexport function __read(o, n) {\n var m = typeof Symbol === \"function\" && o[Symbol.iterator];\n if (!m) return o;\n var i = m.call(o), r, ar = [], e;\n try {\n while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);\n }\n catch (error) { e = { error: error }; }\n finally {\n try {\n if (r && !r.done && (m = i[\"return\"])) m.call(i);\n }\n finally { if (e) throw e.error; }\n }\n return ar;\n}\n\n/** @deprecated */\nexport function __spread() {\n for (var ar = [], i = 0; i < arguments.length; i++)\n ar = ar.concat(__read(arguments[i]));\n return ar;\n}\n\n/** @deprecated */\nexport function __spreadArrays() {\n for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;\n for (var r = Array(s), k = 0, i = 0; i < il; i++)\n for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)\n r[k] = a[j];\n return r;\n}\n\nexport function __spreadArray(to, from, pack) {\n if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {\n if (ar || !(i in from)) {\n if (!ar) ar = Array.prototype.slice.call(from, 0, i);\n ar[i] = from[i];\n }\n }\n return to.concat(ar || Array.prototype.slice.call(from));\n}\n\nexport function __await(v) {\n return this instanceof __await ? (this.v = v, this) : new __await(v);\n}\n\nexport function __asyncGenerator(thisArg, _arguments, generator) {\n if (!Symbol.asyncIterator) throw new TypeError(\"Symbol.asyncIterator is not defined.\");\n var g = generator.apply(thisArg, _arguments || []), i, q = [];\n return i = Object.create((typeof AsyncIterator === \"function\" ? AsyncIterator : Object).prototype), verb(\"next\"), verb(\"throw\"), verb(\"return\", awaitReturn), i[Symbol.asyncIterator] = function () { return this; }, i;\n function awaitReturn(f) { return function (v) { return Promise.resolve(v).then(f, reject); }; }\n function verb(n, f) { if (g[n]) { i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; if (f) i[n] = f(i[n]); } }\n function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } }\n function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); }\n function fulfill(value) { resume(\"next\", value); }\n function reject(value) { resume(\"throw\", value); }\n function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); }\n}\n\nexport function __asyncDelegator(o) {\n var i, p;\n return i = {}, verb(\"next\"), verb(\"throw\", function (e) { throw e; }), verb(\"return\"), i[Symbol.iterator] = function () { return this; }, i;\n function verb(n, f) { i[n] = o[n] ? function (v) { return (p = !p) ? { value: __await(o[n](v)), done: false } : f ? f(v) : v; } : f; }\n}\n\nexport function __asyncValues(o) {\n if (!Symbol.asyncIterator) throw new TypeError(\"Symbol.asyncIterator is not defined.\");\n var m = o[Symbol.asyncIterator], i;\n return m ? m.call(o) : (o = typeof __values === \"function\" ? __values(o) : o[Symbol.iterator](), i = {}, verb(\"next\"), verb(\"throw\"), verb(\"return\"), i[Symbol.asyncIterator] = function () { return this; }, i);\n function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }\n function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }\n}\n\nexport function __makeTemplateObject(cooked, raw) {\n if (Object.defineProperty) { Object.defineProperty(cooked, \"raw\", { value: raw }); } else { cooked.raw = raw; }\n return cooked;\n};\n\nvar __setModuleDefault = Object.create ? (function(o, v) {\n Object.defineProperty(o, \"default\", { enumerable: true, value: v });\n}) : function(o, v) {\n o[\"default\"] = v;\n};\n\nexport function __importStar(mod) {\n if (mod && mod.__esModule) return mod;\n var result = {};\n if (mod != null) for (var k in mod) if (k !== \"default\" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);\n __setModuleDefault(result, mod);\n return result;\n}\n\nexport function __importDefault(mod) {\n return (mod && mod.__esModule) ? mod : { default: mod };\n}\n\nexport function __classPrivateFieldGet(receiver, state, kind, f) {\n if (kind === \"a\" && !f) throw new TypeError(\"Private accessor was defined without a getter\");\n if (typeof state === \"function\" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError(\"Cannot read private member from an object whose class did not declare it\");\n return kind === \"m\" ? f : kind === \"a\" ? f.call(receiver) : f ? f.value : state.get(receiver);\n}\n\nexport function __classPrivateFieldSet(receiver, state, value, kind, f) {\n if (kind === \"m\") throw new TypeError(\"Private method is not writable\");\n if (kind === \"a\" && !f) throw new TypeError(\"Private accessor was defined without a setter\");\n if (typeof state === \"function\" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError(\"Cannot write private member to an object whose class did not declare it\");\n return (kind === \"a\" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;\n}\n\nexport function __classPrivateFieldIn(state, receiver) {\n if (receiver === null || (typeof receiver !== \"object\" && typeof receiver !== \"function\")) throw new TypeError(\"Cannot use 'in' operator on non-object\");\n return typeof state === \"function\" ? receiver === state : state.has(receiver);\n}\n\nexport function __addDisposableResource(env, value, async) {\n if (value !== null && value !== void 0) {\n if (typeof value !== \"object\" && typeof value !== \"function\") throw new TypeError(\"Object expected.\");\n var dispose, inner;\n if (async) {\n if (!Symbol.asyncDispose) throw new TypeError(\"Symbol.asyncDispose is not defined.\");\n dispose = value[Symbol.asyncDispose];\n }\n if (dispose === void 0) {\n if (!Symbol.dispose) throw new TypeError(\"Symbol.dispose is not defined.\");\n dispose = value[Symbol.dispose];\n if (async) inner = dispose;\n }\n if (typeof dispose !== \"function\") throw new TypeError(\"Object not disposable.\");\n if (inner) dispose = function() { try { inner.call(this); } catch (e) { return Promise.reject(e); } };\n env.stack.push({ value: value, dispose: dispose, async: async });\n }\n else if (async) {\n env.stack.push({ async: true });\n }\n return value;\n}\n\nvar _SuppressedError = typeof SuppressedError === \"function\" ? SuppressedError : function (error, suppressed, message) {\n var e = new Error(message);\n return e.name = \"SuppressedError\", e.error = error, e.suppressed = suppressed, e;\n};\n\nexport function __disposeResources(env) {\n function fail(e) {\n env.error = env.hasError ? new _SuppressedError(e, env.error, \"An error was suppressed during disposal.\") : e;\n env.hasError = true;\n }\n var r, s = 0;\n function next() {\n while (r = env.stack.pop()) {\n try {\n if (!r.async && s === 1) return s = 0, env.stack.push(r), Promise.resolve().then(next);\n if (r.dispose) {\n var result = r.dispose.call(r.value);\n if (r.async) return s |= 2, Promise.resolve(result).then(next, function(e) { fail(e); return next(); });\n }\n else s |= 1;\n }\n catch (e) {\n fail(e);\n }\n }\n if (s === 1) return env.hasError ? Promise.reject(env.error) : Promise.resolve();\n if (env.hasError) throw env.error;\n }\n return next();\n}\n\nexport default {\n __extends,\n __assign,\n __rest,\n __decorate,\n __param,\n __metadata,\n __awaiter,\n __generator,\n __createBinding,\n __exportStar,\n __values,\n __read,\n __spread,\n __spreadArrays,\n __spreadArray,\n __await,\n __asyncGenerator,\n __asyncDelegator,\n __asyncValues,\n __makeTemplateObject,\n __importStar,\n __importDefault,\n __classPrivateFieldGet,\n __classPrivateFieldSet,\n __classPrivateFieldIn,\n __addDisposableResource,\n __disposeResources,\n};\n", "/**\n * Returns true if the object is a function.\n * @param value The value to check\n */\nexport function isFunction(value: any): value is (...args: any[]) => any {\n return typeof value === 'function';\n}\n", "/**\n * Used to create Error subclasses until the community moves away from ES5.\n *\n * This is because compiling from TypeScript down to ES5 has issues with subclassing Errors\n * as well as other built-in types: https://github.com/Microsoft/TypeScript/issues/12123\n *\n * @param createImpl A factory function to create the actual constructor implementation. The returned\n * function should be a named function that calls `_super` internally.\n */\nexport function createErrorClass(createImpl: (_super: any) => any): T {\n const _super = (instance: any) => {\n Error.call(instance);\n instance.stack = new Error().stack;\n };\n\n const ctorFunc = createImpl(_super);\n ctorFunc.prototype = Object.create(Error.prototype);\n ctorFunc.prototype.constructor = ctorFunc;\n return ctorFunc;\n}\n", "import { createErrorClass } from './createErrorClass';\n\nexport interface UnsubscriptionError extends Error {\n readonly errors: any[];\n}\n\nexport interface UnsubscriptionErrorCtor {\n /**\n * @deprecated Internal implementation detail. Do not construct error instances.\n * Cannot be tagged as internal: https://github.com/ReactiveX/rxjs/issues/6269\n */\n new (errors: any[]): UnsubscriptionError;\n}\n\n/**\n * An error thrown when one or more errors have occurred during the\n * `unsubscribe` of a {@link Subscription}.\n */\nexport const UnsubscriptionError: UnsubscriptionErrorCtor = createErrorClass(\n (_super) =>\n function UnsubscriptionErrorImpl(this: any, errors: (Error | string)[]) {\n _super(this);\n this.message = errors\n ? `${errors.length} errors occurred during unsubscription:\n${errors.map((err, i) => `${i + 1}) ${err.toString()}`).join('\\n ')}`\n : '';\n this.name = 'UnsubscriptionError';\n this.errors = errors;\n }\n);\n", "/**\n * Removes an item from an array, mutating it.\n * @param arr The array to remove the item from\n * @param item The item to remove\n */\nexport function arrRemove(arr: T[] | undefined | null, item: T) {\n if (arr) {\n const index = arr.indexOf(item);\n 0 <= index && arr.splice(index, 1);\n }\n}\n", "import { isFunction } from './util/isFunction';\nimport { UnsubscriptionError } from './util/UnsubscriptionError';\nimport { SubscriptionLike, TeardownLogic, Unsubscribable } from './types';\nimport { arrRemove } from './util/arrRemove';\n\n/**\n * Represents a disposable resource, such as the execution of an Observable. A\n * Subscription has one important method, `unsubscribe`, that takes no argument\n * and just disposes the resource held by the subscription.\n *\n * Additionally, subscriptions may be grouped together through the `add()`\n * method, which will attach a child Subscription to the current Subscription.\n * When a Subscription is unsubscribed, all its children (and its grandchildren)\n * will be unsubscribed as well.\n */\nexport class Subscription implements SubscriptionLike {\n public static EMPTY = (() => {\n const empty = new Subscription();\n empty.closed = true;\n return empty;\n })();\n\n /**\n * A flag to indicate whether this Subscription has already been unsubscribed.\n */\n public closed = false;\n\n private _parentage: Subscription[] | Subscription | null = null;\n\n /**\n * The list of registered finalizers to execute upon unsubscription. Adding and removing from this\n * list occurs in the {@link #add} and {@link #remove} methods.\n */\n private _finalizers: Exclude[] | null = null;\n\n /**\n * @param initialTeardown A function executed first as part of the finalization\n * process that is kicked off when {@link #unsubscribe} is called.\n */\n constructor(private initialTeardown?: () => void) {}\n\n /**\n * Disposes the resources held by the subscription. May, for instance, cancel\n * an ongoing Observable execution or cancel any other type of work that\n * started when the Subscription was created.\n */\n unsubscribe(): void {\n let errors: any[] | undefined;\n\n if (!this.closed) {\n this.closed = true;\n\n // Remove this from it's parents.\n const { _parentage } = this;\n if (_parentage) {\n this._parentage = null;\n if (Array.isArray(_parentage)) {\n for (const parent of _parentage) {\n parent.remove(this);\n }\n } else {\n _parentage.remove(this);\n }\n }\n\n const { initialTeardown: initialFinalizer } = this;\n if (isFunction(initialFinalizer)) {\n try {\n initialFinalizer();\n } catch (e) {\n errors = e instanceof UnsubscriptionError ? e.errors : [e];\n }\n }\n\n const { _finalizers } = this;\n if (_finalizers) {\n this._finalizers = null;\n for (const finalizer of _finalizers) {\n try {\n execFinalizer(finalizer);\n } catch (err) {\n errors = errors ?? [];\n if (err instanceof UnsubscriptionError) {\n errors = [...errors, ...err.errors];\n } else {\n errors.push(err);\n }\n }\n }\n }\n\n if (errors) {\n throw new UnsubscriptionError(errors);\n }\n }\n }\n\n /**\n * Adds a finalizer to this subscription, so that finalization will be unsubscribed/called\n * when this subscription is unsubscribed. If this subscription is already {@link #closed},\n * because it has already been unsubscribed, then whatever finalizer is passed to it\n * will automatically be executed (unless the finalizer itself is also a closed subscription).\n *\n * Closed Subscriptions cannot be added as finalizers to any subscription. Adding a closed\n * subscription to a any subscription will result in no operation. (A noop).\n *\n * Adding a subscription to itself, or adding `null` or `undefined` will not perform any\n * operation at all. (A noop).\n *\n * `Subscription` instances that are added to this instance will automatically remove themselves\n * if they are unsubscribed. Functions and {@link Unsubscribable} objects that you wish to remove\n * will need to be removed manually with {@link #remove}\n *\n * @param teardown The finalization logic to add to this subscription.\n */\n add(teardown: TeardownLogic): void {\n // Only add the finalizer if it's not undefined\n // and don't add a subscription to itself.\n if (teardown && teardown !== this) {\n if (this.closed) {\n // If this subscription is already closed,\n // execute whatever finalizer is handed to it automatically.\n execFinalizer(teardown);\n } else {\n if (teardown instanceof Subscription) {\n // We don't add closed subscriptions, and we don't add the same subscription\n // twice. Subscription unsubscribe is idempotent.\n if (teardown.closed || teardown._hasParent(this)) {\n return;\n }\n teardown._addParent(this);\n }\n (this._finalizers = this._finalizers ?? []).push(teardown);\n }\n }\n }\n\n /**\n * Checks to see if a this subscription already has a particular parent.\n * This will signal that this subscription has already been added to the parent in question.\n * @param parent the parent to check for\n */\n private _hasParent(parent: Subscription) {\n const { _parentage } = this;\n return _parentage === parent || (Array.isArray(_parentage) && _parentage.includes(parent));\n }\n\n /**\n * Adds a parent to this subscription so it can be removed from the parent if it\n * unsubscribes on it's own.\n *\n * NOTE: THIS ASSUMES THAT {@link _hasParent} HAS ALREADY BEEN CHECKED.\n * @param parent The parent subscription to add\n */\n private _addParent(parent: Subscription) {\n const { _parentage } = this;\n this._parentage = Array.isArray(_parentage) ? (_parentage.push(parent), _parentage) : _parentage ? [_parentage, parent] : parent;\n }\n\n /**\n * Called on a child when it is removed via {@link #remove}.\n * @param parent The parent to remove\n */\n private _removeParent(parent: Subscription) {\n const { _parentage } = this;\n if (_parentage === parent) {\n this._parentage = null;\n } else if (Array.isArray(_parentage)) {\n arrRemove(_parentage, parent);\n }\n }\n\n /**\n * Removes a finalizer from this subscription that was previously added with the {@link #add} method.\n *\n * Note that `Subscription` instances, when unsubscribed, will automatically remove themselves\n * from every other `Subscription` they have been added to. This means that using the `remove` method\n * is not a common thing and should be used thoughtfully.\n *\n * If you add the same finalizer instance of a function or an unsubscribable object to a `Subscription` instance\n * more than once, you will need to call `remove` the same number of times to remove all instances.\n *\n * All finalizer instances are removed to free up memory upon unsubscription.\n *\n * @param teardown The finalizer to remove from this subscription\n */\n remove(teardown: Exclude): void {\n const { _finalizers } = this;\n _finalizers && arrRemove(_finalizers, teardown);\n\n if (teardown instanceof Subscription) {\n teardown._removeParent(this);\n }\n }\n}\n\nexport const EMPTY_SUBSCRIPTION = Subscription.EMPTY;\n\nexport function isSubscription(value: any): value is Subscription {\n return (\n value instanceof Subscription ||\n (value && 'closed' in value && isFunction(value.remove) && isFunction(value.add) && isFunction(value.unsubscribe))\n );\n}\n\nfunction execFinalizer(finalizer: Unsubscribable | (() => void)) {\n if (isFunction(finalizer)) {\n finalizer();\n } else {\n finalizer.unsubscribe();\n }\n}\n", "import { Subscriber } from './Subscriber';\nimport { ObservableNotification } from './types';\n\n/**\n * The {@link GlobalConfig} object for RxJS. It is used to configure things\n * like how to react on unhandled errors.\n */\nexport const config: GlobalConfig = {\n onUnhandledError: null,\n onStoppedNotification: null,\n Promise: undefined,\n useDeprecatedSynchronousErrorHandling: false,\n useDeprecatedNextContext: false,\n};\n\n/**\n * The global configuration object for RxJS, used to configure things\n * like how to react on unhandled errors. Accessible via {@link config}\n * object.\n */\nexport interface GlobalConfig {\n /**\n * A registration point for unhandled errors from RxJS. These are errors that\n * cannot were not handled by consuming code in the usual subscription path. For\n * example, if you have this configured, and you subscribe to an observable without\n * providing an error handler, errors from that subscription will end up here. This\n * will _always_ be called asynchronously on another job in the runtime. This is because\n * we do not want errors thrown in this user-configured handler to interfere with the\n * behavior of the library.\n */\n onUnhandledError: ((err: any) => void) | null;\n\n /**\n * A registration point for notifications that cannot be sent to subscribers because they\n * have completed, errored or have been explicitly unsubscribed. By default, next, complete\n * and error notifications sent to stopped subscribers are noops. However, sometimes callers\n * might want a different behavior. For example, with sources that attempt to report errors\n * to stopped subscribers, a caller can configure RxJS to throw an unhandled error instead.\n * This will _always_ be called asynchronously on another job in the runtime. This is because\n * we do not want errors thrown in this user-configured handler to interfere with the\n * behavior of the library.\n */\n onStoppedNotification: ((notification: ObservableNotification, subscriber: Subscriber) => void) | null;\n\n /**\n * The promise constructor used by default for {@link Observable#toPromise toPromise} and {@link Observable#forEach forEach}\n * methods.\n *\n * @deprecated As of version 8, RxJS will no longer support this sort of injection of a\n * Promise constructor. If you need a Promise implementation other than native promises,\n * please polyfill/patch Promise as you see appropriate. Will be removed in v8.\n */\n Promise?: PromiseConstructorLike;\n\n /**\n * If true, turns on synchronous error rethrowing, which is a deprecated behavior\n * in v6 and higher. This behavior enables bad patterns like wrapping a subscribe\n * call in a try/catch block. It also enables producer interference, a nasty bug\n * where a multicast can be broken for all observers by a downstream consumer with\n * an unhandled error. DO NOT USE THIS FLAG UNLESS IT'S NEEDED TO BUY TIME\n * FOR MIGRATION REASONS.\n *\n * @deprecated As of version 8, RxJS will no longer support synchronous throwing\n * of unhandled errors. All errors will be thrown on a separate call stack to prevent bad\n * behaviors described above. Will be removed in v8.\n */\n useDeprecatedSynchronousErrorHandling: boolean;\n\n /**\n * If true, enables an as-of-yet undocumented feature from v5: The ability to access\n * `unsubscribe()` via `this` context in `next` functions created in observers passed\n * to `subscribe`.\n *\n * This is being removed because the performance was severely problematic, and it could also cause\n * issues when types other than POJOs are passed to subscribe as subscribers, as they will likely have\n * their `this` context overwritten.\n *\n * @deprecated As of version 8, RxJS will no longer support altering the\n * context of next functions provided as part of an observer to Subscribe. Instead,\n * you will have access to a subscription or a signal or token that will allow you to do things like\n * unsubscribe and test closed status. Will be removed in v8.\n */\n useDeprecatedNextContext: boolean;\n}\n", "import type { TimerHandle } from './timerHandle';\ntype SetTimeoutFunction = (handler: () => void, timeout?: number, ...args: any[]) => TimerHandle;\ntype ClearTimeoutFunction = (handle: TimerHandle) => void;\n\ninterface TimeoutProvider {\n setTimeout: SetTimeoutFunction;\n clearTimeout: ClearTimeoutFunction;\n delegate:\n | {\n setTimeout: SetTimeoutFunction;\n clearTimeout: ClearTimeoutFunction;\n }\n | undefined;\n}\n\nexport const timeoutProvider: TimeoutProvider = {\n // When accessing the delegate, use the variable rather than `this` so that\n // the functions can be called without being bound to the provider.\n setTimeout(handler: () => void, timeout?: number, ...args) {\n const { delegate } = timeoutProvider;\n if (delegate?.setTimeout) {\n return delegate.setTimeout(handler, timeout, ...args);\n }\n return setTimeout(handler, timeout, ...args);\n },\n clearTimeout(handle) {\n const { delegate } = timeoutProvider;\n return (delegate?.clearTimeout || clearTimeout)(handle as any);\n },\n delegate: undefined,\n};\n", "import { config } from '../config';\nimport { timeoutProvider } from '../scheduler/timeoutProvider';\n\n/**\n * Handles an error on another job either with the user-configured {@link onUnhandledError},\n * or by throwing it on that new job so it can be picked up by `window.onerror`, `process.on('error')`, etc.\n *\n * This should be called whenever there is an error that is out-of-band with the subscription\n * or when an error hits a terminal boundary of the subscription and no error handler was provided.\n *\n * @param err the error to report\n */\nexport function reportUnhandledError(err: any) {\n timeoutProvider.setTimeout(() => {\n const { onUnhandledError } = config;\n if (onUnhandledError) {\n // Execute the user-configured error handler.\n onUnhandledError(err);\n } else {\n // Throw so it is picked up by the runtime's uncaught error mechanism.\n throw err;\n }\n });\n}\n", "/* tslint:disable:no-empty */\nexport function noop() { }\n", "import { CompleteNotification, NextNotification, ErrorNotification } from './types';\n\n/**\n * A completion object optimized for memory use and created to be the\n * same \"shape\" as other notifications in v8.\n * @internal\n */\nexport const COMPLETE_NOTIFICATION = (() => createNotification('C', undefined, undefined) as CompleteNotification)();\n\n/**\n * Internal use only. Creates an optimized error notification that is the same \"shape\"\n * as other notifications.\n * @internal\n */\nexport function errorNotification(error: any): ErrorNotification {\n return createNotification('E', undefined, error) as any;\n}\n\n/**\n * Internal use only. Creates an optimized next notification that is the same \"shape\"\n * as other notifications.\n * @internal\n */\nexport function nextNotification(value: T) {\n return createNotification('N', value, undefined) as NextNotification;\n}\n\n/**\n * Ensures that all notifications created internally have the same \"shape\" in v8.\n *\n * TODO: This is only exported to support a crazy legacy test in `groupBy`.\n * @internal\n */\nexport function createNotification(kind: 'N' | 'E' | 'C', value: any, error: any) {\n return {\n kind,\n value,\n error,\n };\n}\n", "import { config } from '../config';\n\nlet context: { errorThrown: boolean; error: any } | null = null;\n\n/**\n * Handles dealing with errors for super-gross mode. Creates a context, in which\n * any synchronously thrown errors will be passed to {@link captureError}. Which\n * will record the error such that it will be rethrown after the call back is complete.\n * TODO: Remove in v8\n * @param cb An immediately executed function.\n */\nexport function errorContext(cb: () => void) {\n if (config.useDeprecatedSynchronousErrorHandling) {\n const isRoot = !context;\n if (isRoot) {\n context = { errorThrown: false, error: null };\n }\n cb();\n if (isRoot) {\n const { errorThrown, error } = context!;\n context = null;\n if (errorThrown) {\n throw error;\n }\n }\n } else {\n // This is the general non-deprecated path for everyone that\n // isn't crazy enough to use super-gross mode (useDeprecatedSynchronousErrorHandling)\n cb();\n }\n}\n\n/**\n * Captures errors only in super-gross mode.\n * @param err the error to capture\n */\nexport function captureError(err: any) {\n if (config.useDeprecatedSynchronousErrorHandling && context) {\n context.errorThrown = true;\n context.error = err;\n }\n}\n", "import { isFunction } from './util/isFunction';\nimport { Observer, ObservableNotification } from './types';\nimport { isSubscription, Subscription } from './Subscription';\nimport { config } from './config';\nimport { reportUnhandledError } from './util/reportUnhandledError';\nimport { noop } from './util/noop';\nimport { nextNotification, errorNotification, COMPLETE_NOTIFICATION } from './NotificationFactories';\nimport { timeoutProvider } from './scheduler/timeoutProvider';\nimport { captureError } from './util/errorContext';\n\n/**\n * Implements the {@link Observer} interface and extends the\n * {@link Subscription} class. While the {@link Observer} is the public API for\n * consuming the values of an {@link Observable}, all Observers get converted to\n * a Subscriber, in order to provide Subscription-like capabilities such as\n * `unsubscribe`. Subscriber is a common type in RxJS, and crucial for\n * implementing operators, but it is rarely used as a public API.\n */\nexport class Subscriber extends Subscription implements Observer {\n /**\n * A static factory for a Subscriber, given a (potentially partial) definition\n * of an Observer.\n * @param next The `next` callback of an Observer.\n * @param error The `error` callback of an\n * Observer.\n * @param complete The `complete` callback of an\n * Observer.\n * @return A Subscriber wrapping the (partially defined)\n * Observer represented by the given arguments.\n * @deprecated Do not use. Will be removed in v8. There is no replacement for this\n * method, and there is no reason to be creating instances of `Subscriber` directly.\n * If you have a specific use case, please file an issue.\n */\n static create(next?: (x?: T) => void, error?: (e?: any) => void, complete?: () => void): Subscriber {\n return new SafeSubscriber(next, error, complete);\n }\n\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n protected isStopped: boolean = false;\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n protected destination: Subscriber | Observer; // this `any` is the escape hatch to erase extra type param (e.g. R)\n\n /**\n * @deprecated Internal implementation detail, do not use directly. Will be made internal in v8.\n * There is no reason to directly create an instance of Subscriber. This type is exported for typings reasons.\n */\n constructor(destination?: Subscriber | Observer) {\n super();\n if (destination) {\n this.destination = destination;\n // Automatically chain subscriptions together here.\n // if destination is a Subscription, then it is a Subscriber.\n if (isSubscription(destination)) {\n destination.add(this);\n }\n } else {\n this.destination = EMPTY_OBSERVER;\n }\n }\n\n /**\n * The {@link Observer} callback to receive notifications of type `next` from\n * the Observable, with a value. The Observable may call this method 0 or more\n * times.\n * @param value The `next` value.\n */\n next(value: T): void {\n if (this.isStopped) {\n handleStoppedNotification(nextNotification(value), this);\n } else {\n this._next(value!);\n }\n }\n\n /**\n * The {@link Observer} callback to receive notifications of type `error` from\n * the Observable, with an attached `Error`. Notifies the Observer that\n * the Observable has experienced an error condition.\n * @param err The `error` exception.\n */\n error(err?: any): void {\n if (this.isStopped) {\n handleStoppedNotification(errorNotification(err), this);\n } else {\n this.isStopped = true;\n this._error(err);\n }\n }\n\n /**\n * The {@link Observer} callback to receive a valueless notification of type\n * `complete` from the Observable. Notifies the Observer that the Observable\n * has finished sending push-based notifications.\n */\n complete(): void {\n if (this.isStopped) {\n handleStoppedNotification(COMPLETE_NOTIFICATION, this);\n } else {\n this.isStopped = true;\n this._complete();\n }\n }\n\n unsubscribe(): void {\n if (!this.closed) {\n this.isStopped = true;\n super.unsubscribe();\n this.destination = null!;\n }\n }\n\n protected _next(value: T): void {\n this.destination.next(value);\n }\n\n protected _error(err: any): void {\n try {\n this.destination.error(err);\n } finally {\n this.unsubscribe();\n }\n }\n\n protected _complete(): void {\n try {\n this.destination.complete();\n } finally {\n this.unsubscribe();\n }\n }\n}\n\n/**\n * This bind is captured here because we want to be able to have\n * compatibility with monoid libraries that tend to use a method named\n * `bind`. In particular, a library called Monio requires this.\n */\nconst _bind = Function.prototype.bind;\n\nfunction bind any>(fn: Fn, thisArg: any): Fn {\n return _bind.call(fn, thisArg);\n}\n\n/**\n * Internal optimization only, DO NOT EXPOSE.\n * @internal\n */\nclass ConsumerObserver implements Observer {\n constructor(private partialObserver: Partial>) {}\n\n next(value: T): void {\n const { partialObserver } = this;\n if (partialObserver.next) {\n try {\n partialObserver.next(value);\n } catch (error) {\n handleUnhandledError(error);\n }\n }\n }\n\n error(err: any): void {\n const { partialObserver } = this;\n if (partialObserver.error) {\n try {\n partialObserver.error(err);\n } catch (error) {\n handleUnhandledError(error);\n }\n } else {\n handleUnhandledError(err);\n }\n }\n\n complete(): void {\n const { partialObserver } = this;\n if (partialObserver.complete) {\n try {\n partialObserver.complete();\n } catch (error) {\n handleUnhandledError(error);\n }\n }\n }\n}\n\nexport class SafeSubscriber extends Subscriber {\n constructor(\n observerOrNext?: Partial> | ((value: T) => void) | null,\n error?: ((e?: any) => void) | null,\n complete?: (() => void) | null\n ) {\n super();\n\n let partialObserver: Partial>;\n if (isFunction(observerOrNext) || !observerOrNext) {\n // The first argument is a function, not an observer. The next\n // two arguments *could* be observers, or they could be empty.\n partialObserver = {\n next: (observerOrNext ?? undefined) as ((value: T) => void) | undefined,\n error: error ?? undefined,\n complete: complete ?? undefined,\n };\n } else {\n // The first argument is a partial observer.\n let context: any;\n if (this && config.useDeprecatedNextContext) {\n // This is a deprecated path that made `this.unsubscribe()` available in\n // next handler functions passed to subscribe. This only exists behind a flag\n // now, as it is *very* slow.\n context = Object.create(observerOrNext);\n context.unsubscribe = () => this.unsubscribe();\n partialObserver = {\n next: observerOrNext.next && bind(observerOrNext.next, context),\n error: observerOrNext.error && bind(observerOrNext.error, context),\n complete: observerOrNext.complete && bind(observerOrNext.complete, context),\n };\n } else {\n // The \"normal\" path. Just use the partial observer directly.\n partialObserver = observerOrNext;\n }\n }\n\n // Wrap the partial observer to ensure it's a full observer, and\n // make sure proper error handling is accounted for.\n this.destination = new ConsumerObserver(partialObserver);\n }\n}\n\nfunction handleUnhandledError(error: any) {\n if (config.useDeprecatedSynchronousErrorHandling) {\n captureError(error);\n } else {\n // Ideal path, we report this as an unhandled error,\n // which is thrown on a new call stack.\n reportUnhandledError(error);\n }\n}\n\n/**\n * An error handler used when no error handler was supplied\n * to the SafeSubscriber -- meaning no error handler was supplied\n * do the `subscribe` call on our observable.\n * @param err The error to handle\n */\nfunction defaultErrorHandler(err: any) {\n throw err;\n}\n\n/**\n * A handler for notifications that cannot be sent to a stopped subscriber.\n * @param notification The notification being sent.\n * @param subscriber The stopped subscriber.\n */\nfunction handleStoppedNotification(notification: ObservableNotification, subscriber: Subscriber) {\n const { onStoppedNotification } = config;\n onStoppedNotification && timeoutProvider.setTimeout(() => onStoppedNotification(notification, subscriber));\n}\n\n/**\n * The observer used as a stub for subscriptions where the user did not\n * pass any arguments to `subscribe`. Comes with the default error handling\n * behavior.\n */\nexport const EMPTY_OBSERVER: Readonly> & { closed: true } = {\n closed: true,\n next: noop,\n error: defaultErrorHandler,\n complete: noop,\n};\n", "/**\n * Symbol.observable or a string \"@@observable\". Used for interop\n *\n * @deprecated We will no longer be exporting this symbol in upcoming versions of RxJS.\n * Instead polyfill and use Symbol.observable directly *or* use https://www.npmjs.com/package/symbol-observable\n */\nexport const observable: string | symbol = (() => (typeof Symbol === 'function' && Symbol.observable) || '@@observable')();\n", "/**\n * This function takes one parameter and just returns it. Simply put,\n * this is like `(x: T): T => x`.\n *\n * ## Examples\n *\n * This is useful in some cases when using things like `mergeMap`\n *\n * ```ts\n * import { interval, take, map, range, mergeMap, identity } from 'rxjs';\n *\n * const source$ = interval(1000).pipe(take(5));\n *\n * const result$ = source$.pipe(\n * map(i => range(i)),\n * mergeMap(identity) // same as mergeMap(x => x)\n * );\n *\n * result$.subscribe({\n * next: console.log\n * });\n * ```\n *\n * Or when you want to selectively apply an operator\n *\n * ```ts\n * import { interval, take, identity } from 'rxjs';\n *\n * const shouldLimit = () => Math.random() < 0.5;\n *\n * const source$ = interval(1000);\n *\n * const result$ = source$.pipe(shouldLimit() ? take(5) : identity);\n *\n * result$.subscribe({\n * next: console.log\n * });\n * ```\n *\n * @param x Any value that is returned by this function\n * @returns The value passed as the first parameter to this function\n */\nexport function identity(x: T): T {\n return x;\n}\n", "import { identity } from './identity';\nimport { UnaryFunction } from '../types';\n\nexport function pipe(): typeof identity;\nexport function pipe(fn1: UnaryFunction): UnaryFunction;\nexport function pipe(fn1: UnaryFunction, fn2: UnaryFunction): UnaryFunction;\nexport function pipe(fn1: UnaryFunction, fn2: UnaryFunction, fn3: UnaryFunction): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction,\n fn6: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction,\n fn6: UnaryFunction,\n fn7: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction,\n fn6: UnaryFunction,\n fn7: UnaryFunction,\n fn8: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction,\n fn6: UnaryFunction,\n fn7: UnaryFunction,\n fn8: UnaryFunction,\n fn9: UnaryFunction\n): UnaryFunction;\nexport function pipe(\n fn1: UnaryFunction,\n fn2: UnaryFunction,\n fn3: UnaryFunction,\n fn4: UnaryFunction,\n fn5: UnaryFunction,\n fn6: UnaryFunction,\n fn7: UnaryFunction,\n fn8: UnaryFunction,\n fn9: UnaryFunction,\n ...fns: UnaryFunction[]\n): UnaryFunction;\n\n/**\n * pipe() can be called on one or more functions, each of which can take one argument (\"UnaryFunction\")\n * and uses it to return a value.\n * It returns a function that takes one argument, passes it to the first UnaryFunction, and then\n * passes the result to the next one, passes that result to the next one, and so on. \n */\nexport function pipe(...fns: Array>): UnaryFunction {\n return pipeFromArray(fns);\n}\n\n/** @internal */\nexport function pipeFromArray(fns: Array>): UnaryFunction {\n if (fns.length === 0) {\n return identity as UnaryFunction;\n }\n\n if (fns.length === 1) {\n return fns[0];\n }\n\n return function piped(input: T): R {\n return fns.reduce((prev: any, fn: UnaryFunction) => fn(prev), input as any);\n };\n}\n", "import { Operator } from './Operator';\nimport { SafeSubscriber, Subscriber } from './Subscriber';\nimport { isSubscription, Subscription } from './Subscription';\nimport { TeardownLogic, OperatorFunction, Subscribable, Observer } from './types';\nimport { observable as Symbol_observable } from './symbol/observable';\nimport { pipeFromArray } from './util/pipe';\nimport { config } from './config';\nimport { isFunction } from './util/isFunction';\nimport { errorContext } from './util/errorContext';\n\n/**\n * A representation of any set of values over any amount of time. This is the most basic building block\n * of RxJS.\n */\nexport class Observable implements Subscribable {\n /**\n * @deprecated Internal implementation detail, do not use directly. Will be made internal in v8.\n */\n source: Observable | undefined;\n\n /**\n * @deprecated Internal implementation detail, do not use directly. Will be made internal in v8.\n */\n operator: Operator | undefined;\n\n /**\n * @param subscribe The function that is called when the Observable is\n * initially subscribed to. This function is given a Subscriber, to which new values\n * can be `next`ed, or an `error` method can be called to raise an error, or\n * `complete` can be called to notify of a successful completion.\n */\n constructor(subscribe?: (this: Observable, subscriber: Subscriber) => TeardownLogic) {\n if (subscribe) {\n this._subscribe = subscribe;\n }\n }\n\n // HACK: Since TypeScript inherits static properties too, we have to\n // fight against TypeScript here so Subject can have a different static create signature\n /**\n * Creates a new Observable by calling the Observable constructor\n * @param subscribe the subscriber function to be passed to the Observable constructor\n * @return A new observable.\n * @deprecated Use `new Observable()` instead. Will be removed in v8.\n */\n static create: (...args: any[]) => any = (subscribe?: (subscriber: Subscriber) => TeardownLogic) => {\n return new Observable(subscribe);\n };\n\n /**\n * Creates a new Observable, with this Observable instance as the source, and the passed\n * operator defined as the new observable's operator.\n * @param operator the operator defining the operation to take on the observable\n * @return A new observable with the Operator applied.\n * @deprecated Internal implementation detail, do not use directly. Will be made internal in v8.\n * If you have implemented an operator using `lift`, it is recommended that you create an\n * operator by simply returning `new Observable()` directly. See \"Creating new operators from\n * scratch\" section here: https://rxjs.dev/guide/operators\n */\n lift(operator?: Operator): Observable {\n const observable = new Observable();\n observable.source = this;\n observable.operator = operator;\n return observable;\n }\n\n subscribe(observerOrNext?: Partial> | ((value: T) => void)): Subscription;\n /** @deprecated Instead of passing separate callback arguments, use an observer argument. Signatures taking separate callback arguments will be removed in v8. Details: https://rxjs.dev/deprecations/subscribe-arguments */\n subscribe(next?: ((value: T) => void) | null, error?: ((error: any) => void) | null, complete?: (() => void) | null): Subscription;\n /**\n * Invokes an execution of an Observable and registers Observer handlers for notifications it will emit.\n *\n * Use it when you have all these Observables, but still nothing is happening.\n *\n * `subscribe` is not a regular operator, but a method that calls Observable's internal `subscribe` function. It\n * might be for example a function that you passed to Observable's constructor, but most of the time it is\n * a library implementation, which defines what will be emitted by an Observable, and when it be will emitted. This means\n * that calling `subscribe` is actually the moment when Observable starts its work, not when it is created, as it is often\n * the thought.\n *\n * Apart from starting the execution of an Observable, this method allows you to listen for values\n * that an Observable emits, as well as for when it completes or errors. You can achieve this in two\n * of the following ways.\n *\n * The first way is creating an object that implements {@link Observer} interface. It should have methods\n * defined by that interface, but note that it should be just a regular JavaScript object, which you can create\n * yourself in any way you want (ES6 class, classic function constructor, object literal etc.). In particular, do\n * not attempt to use any RxJS implementation details to create Observers - you don't need them. Remember also\n * that your object does not have to implement all methods. If you find yourself creating a method that doesn't\n * do anything, you can simply omit it. Note however, if the `error` method is not provided and an error happens,\n * it will be thrown asynchronously. Errors thrown asynchronously cannot be caught using `try`/`catch`. Instead,\n * use the {@link onUnhandledError} configuration option or use a runtime handler (like `window.onerror` or\n * `process.on('error)`) to be notified of unhandled errors. Because of this, it's recommended that you provide\n * an `error` method to avoid missing thrown errors.\n *\n * The second way is to give up on Observer object altogether and simply provide callback functions in place of its methods.\n * This means you can provide three functions as arguments to `subscribe`, where the first function is equivalent\n * of a `next` method, the second of an `error` method and the third of a `complete` method. Just as in case of an Observer,\n * if you do not need to listen for something, you can omit a function by passing `undefined` or `null`,\n * since `subscribe` recognizes these functions by where they were placed in function call. When it comes\n * to the `error` function, as with an Observer, if not provided, errors emitted by an Observable will be thrown asynchronously.\n *\n * You can, however, subscribe with no parameters at all. This may be the case where you're not interested in terminal events\n * and you also handled emissions internally by using operators (e.g. using `tap`).\n *\n * Whichever style of calling `subscribe` you use, in both cases it returns a Subscription object.\n * This object allows you to call `unsubscribe` on it, which in turn will stop the work that an Observable does and will clean\n * up all resources that an Observable used. Note that cancelling a subscription will not call `complete` callback\n * provided to `subscribe` function, which is reserved for a regular completion signal that comes from an Observable.\n *\n * Remember that callbacks provided to `subscribe` are not guaranteed to be called asynchronously.\n * It is an Observable itself that decides when these functions will be called. For example {@link of}\n * by default emits all its values synchronously. Always check documentation for how given Observable\n * will behave when subscribed and if its default behavior can be modified with a `scheduler`.\n *\n * #### Examples\n *\n * Subscribe with an {@link guide/observer Observer}\n *\n * ```ts\n * import { of } from 'rxjs';\n *\n * const sumObserver = {\n * sum: 0,\n * next(value) {\n * console.log('Adding: ' + value);\n * this.sum = this.sum + value;\n * },\n * error() {\n * // We actually could just remove this method,\n * // since we do not really care about errors right now.\n * },\n * complete() {\n * console.log('Sum equals: ' + this.sum);\n * }\n * };\n *\n * of(1, 2, 3) // Synchronously emits 1, 2, 3 and then completes.\n * .subscribe(sumObserver);\n *\n * // Logs:\n * // 'Adding: 1'\n * // 'Adding: 2'\n * // 'Adding: 3'\n * // 'Sum equals: 6'\n * ```\n *\n * Subscribe with functions ({@link deprecations/subscribe-arguments deprecated})\n *\n * ```ts\n * import { of } from 'rxjs'\n *\n * let sum = 0;\n *\n * of(1, 2, 3).subscribe(\n * value => {\n * console.log('Adding: ' + value);\n * sum = sum + value;\n * },\n * undefined,\n * () => console.log('Sum equals: ' + sum)\n * );\n *\n * // Logs:\n * // 'Adding: 1'\n * // 'Adding: 2'\n * // 'Adding: 3'\n * // 'Sum equals: 6'\n * ```\n *\n * Cancel a subscription\n *\n * ```ts\n * import { interval } from 'rxjs';\n *\n * const subscription = interval(1000).subscribe({\n * next(num) {\n * console.log(num)\n * },\n * complete() {\n * // Will not be called, even when cancelling subscription.\n * console.log('completed!');\n * }\n * });\n *\n * setTimeout(() => {\n * subscription.unsubscribe();\n * console.log('unsubscribed!');\n * }, 2500);\n *\n * // Logs:\n * // 0 after 1s\n * // 1 after 2s\n * // 'unsubscribed!' after 2.5s\n * ```\n *\n * @param observerOrNext Either an {@link Observer} with some or all callback methods,\n * or the `next` handler that is called for each value emitted from the subscribed Observable.\n * @param error A handler for a terminal event resulting from an error. If no error handler is provided,\n * the error will be thrown asynchronously as unhandled.\n * @param complete A handler for a terminal event resulting from successful completion.\n * @return A subscription reference to the registered handlers.\n */\n subscribe(\n observerOrNext?: Partial> | ((value: T) => void) | null,\n error?: ((error: any) => void) | null,\n complete?: (() => void) | null\n ): Subscription {\n const subscriber = isSubscriber(observerOrNext) ? observerOrNext : new SafeSubscriber(observerOrNext, error, complete);\n\n errorContext(() => {\n const { operator, source } = this;\n subscriber.add(\n operator\n ? // We're dealing with a subscription in the\n // operator chain to one of our lifted operators.\n operator.call(subscriber, source)\n : source\n ? // If `source` has a value, but `operator` does not, something that\n // had intimate knowledge of our API, like our `Subject`, must have\n // set it. We're going to just call `_subscribe` directly.\n this._subscribe(subscriber)\n : // In all other cases, we're likely wrapping a user-provided initializer\n // function, so we need to catch errors and handle them appropriately.\n this._trySubscribe(subscriber)\n );\n });\n\n return subscriber;\n }\n\n /** @internal */\n protected _trySubscribe(sink: Subscriber): TeardownLogic {\n try {\n return this._subscribe(sink);\n } catch (err) {\n // We don't need to return anything in this case,\n // because it's just going to try to `add()` to a subscription\n // above.\n sink.error(err);\n }\n }\n\n /**\n * Used as a NON-CANCELLABLE means of subscribing to an observable, for use with\n * APIs that expect promises, like `async/await`. You cannot unsubscribe from this.\n *\n * **WARNING**: Only use this with observables you *know* will complete. If the source\n * observable does not complete, you will end up with a promise that is hung up, and\n * potentially all of the state of an async function hanging out in memory. To avoid\n * this situation, look into adding something like {@link timeout}, {@link take},\n * {@link takeWhile}, or {@link takeUntil} amongst others.\n *\n * #### Example\n *\n * ```ts\n * import { interval, take } from 'rxjs';\n *\n * const source$ = interval(1000).pipe(take(4));\n *\n * async function getTotal() {\n * let total = 0;\n *\n * await source$.forEach(value => {\n * total += value;\n * console.log('observable -> ' + value);\n * });\n *\n * return total;\n * }\n *\n * getTotal().then(\n * total => console.log('Total: ' + total)\n * );\n *\n * // Expected:\n * // 'observable -> 0'\n * // 'observable -> 1'\n * // 'observable -> 2'\n * // 'observable -> 3'\n * // 'Total: 6'\n * ```\n *\n * @param next A handler for each value emitted by the observable.\n * @return A promise that either resolves on observable completion or\n * rejects with the handled error.\n */\n forEach(next: (value: T) => void): Promise;\n\n /**\n * @param next a handler for each value emitted by the observable\n * @param promiseCtor a constructor function used to instantiate the Promise\n * @return a promise that either resolves on observable completion or\n * rejects with the handled error\n * @deprecated Passing a Promise constructor will no longer be available\n * in upcoming versions of RxJS. This is because it adds weight to the library, for very\n * little benefit. If you need this functionality, it is recommended that you either\n * polyfill Promise, or you create an adapter to convert the returned native promise\n * to whatever promise implementation you wanted. Will be removed in v8.\n */\n forEach(next: (value: T) => void, promiseCtor: PromiseConstructorLike): Promise;\n\n forEach(next: (value: T) => void, promiseCtor?: PromiseConstructorLike): Promise {\n promiseCtor = getPromiseCtor(promiseCtor);\n\n return new promiseCtor((resolve, reject) => {\n const subscriber = new SafeSubscriber({\n next: (value) => {\n try {\n next(value);\n } catch (err) {\n reject(err);\n subscriber.unsubscribe();\n }\n },\n error: reject,\n complete: resolve,\n });\n this.subscribe(subscriber);\n }) as Promise;\n }\n\n /** @internal */\n protected _subscribe(subscriber: Subscriber): TeardownLogic {\n return this.source?.subscribe(subscriber);\n }\n\n /**\n * An interop point defined by the es7-observable spec https://github.com/zenparsing/es-observable\n * @return This instance of the observable.\n */\n [Symbol_observable]() {\n return this;\n }\n\n /* tslint:disable:max-line-length */\n pipe(): Observable;\n pipe(op1: OperatorFunction): Observable;\n pipe(op1: OperatorFunction, op2: OperatorFunction): Observable;\n pipe(op1: OperatorFunction, op2: OperatorFunction, op3: OperatorFunction): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction,\n op6: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction,\n op6: OperatorFunction,\n op7: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction,\n op6: OperatorFunction,\n op7: OperatorFunction,\n op8: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction,\n op6: OperatorFunction,\n op7: OperatorFunction,\n op8: OperatorFunction,\n op9: OperatorFunction\n ): Observable;\n pipe(\n op1: OperatorFunction,\n op2: OperatorFunction,\n op3: OperatorFunction,\n op4: OperatorFunction,\n op5: OperatorFunction,\n op6: OperatorFunction,\n op7: OperatorFunction,\n op8: OperatorFunction,\n op9: OperatorFunction,\n ...operations: OperatorFunction[]\n ): Observable;\n /* tslint:enable:max-line-length */\n\n /**\n * Used to stitch together functional operators into a chain.\n *\n * ## Example\n *\n * ```ts\n * import { interval, filter, map, scan } from 'rxjs';\n *\n * interval(1000)\n * .pipe(\n * filter(x => x % 2 === 0),\n * map(x => x + x),\n * scan((acc, x) => acc + x)\n * )\n * .subscribe(x => console.log(x));\n * ```\n *\n * @return The Observable result of all the operators having been called\n * in the order they were passed in.\n */\n pipe(...operations: OperatorFunction[]): Observable {\n return pipeFromArray(operations)(this);\n }\n\n /* tslint:disable:max-line-length */\n /** @deprecated Replaced with {@link firstValueFrom} and {@link lastValueFrom}. Will be removed in v8. Details: https://rxjs.dev/deprecations/to-promise */\n toPromise(): Promise;\n /** @deprecated Replaced with {@link firstValueFrom} and {@link lastValueFrom}. Will be removed in v8. Details: https://rxjs.dev/deprecations/to-promise */\n toPromise(PromiseCtor: typeof Promise): Promise;\n /** @deprecated Replaced with {@link firstValueFrom} and {@link lastValueFrom}. Will be removed in v8. Details: https://rxjs.dev/deprecations/to-promise */\n toPromise(PromiseCtor: PromiseConstructorLike): Promise;\n /* tslint:enable:max-line-length */\n\n /**\n * Subscribe to this Observable and get a Promise resolving on\n * `complete` with the last emission (if any).\n *\n * **WARNING**: Only use this with observables you *know* will complete. If the source\n * observable does not complete, you will end up with a promise that is hung up, and\n * potentially all of the state of an async function hanging out in memory. To avoid\n * this situation, look into adding something like {@link timeout}, {@link take},\n * {@link takeWhile}, or {@link takeUntil} amongst others.\n *\n * @param [promiseCtor] a constructor function used to instantiate\n * the Promise\n * @return A Promise that resolves with the last value emit, or\n * rejects on an error. If there were no emissions, Promise\n * resolves with undefined.\n * @deprecated Replaced with {@link firstValueFrom} and {@link lastValueFrom}. Will be removed in v8. Details: https://rxjs.dev/deprecations/to-promise\n */\n toPromise(promiseCtor?: PromiseConstructorLike): Promise {\n promiseCtor = getPromiseCtor(promiseCtor);\n\n return new promiseCtor((resolve, reject) => {\n let value: T | undefined;\n this.subscribe(\n (x: T) => (value = x),\n (err: any) => reject(err),\n () => resolve(value)\n );\n }) as Promise;\n }\n}\n\n/**\n * Decides between a passed promise constructor from consuming code,\n * A default configured promise constructor, and the native promise\n * constructor and returns it. If nothing can be found, it will throw\n * an error.\n * @param promiseCtor The optional promise constructor to passed by consuming code\n */\nfunction getPromiseCtor(promiseCtor: PromiseConstructorLike | undefined) {\n return promiseCtor ?? config.Promise ?? Promise;\n}\n\nfunction isObserver(value: any): value is Observer {\n return value && isFunction(value.next) && isFunction(value.error) && isFunction(value.complete);\n}\n\nfunction isSubscriber(value: any): value is Subscriber {\n return (value && value instanceof Subscriber) || (isObserver(value) && isSubscription(value));\n}\n", "import { Observable } from '../Observable';\nimport { Subscriber } from '../Subscriber';\nimport { OperatorFunction } from '../types';\nimport { isFunction } from './isFunction';\n\n/**\n * Used to determine if an object is an Observable with a lift function.\n */\nexport function hasLift(source: any): source is { lift: InstanceType['lift'] } {\n return isFunction(source?.lift);\n}\n\n/**\n * Creates an `OperatorFunction`. Used to define operators throughout the library in a concise way.\n * @param init The logic to connect the liftedSource to the subscriber at the moment of subscription.\n */\nexport function operate(\n init: (liftedSource: Observable, subscriber: Subscriber) => (() => void) | void\n): OperatorFunction {\n return (source: Observable) => {\n if (hasLift(source)) {\n return source.lift(function (this: Subscriber, liftedSource: Observable) {\n try {\n return init(liftedSource, this);\n } catch (err) {\n this.error(err);\n }\n });\n }\n throw new TypeError('Unable to lift unknown Observable type');\n };\n}\n", "import { Subscriber } from '../Subscriber';\n\n/**\n * Creates an instance of an `OperatorSubscriber`.\n * @param destination The downstream subscriber.\n * @param onNext Handles next values, only called if this subscriber is not stopped or closed. Any\n * error that occurs in this function is caught and sent to the `error` method of this subscriber.\n * @param onError Handles errors from the subscription, any errors that occur in this handler are caught\n * and send to the `destination` error handler.\n * @param onComplete Handles completion notification from the subscription. Any errors that occur in\n * this handler are sent to the `destination` error handler.\n * @param onFinalize Additional teardown logic here. This will only be called on teardown if the\n * subscriber itself is not already closed. This is called after all other teardown logic is executed.\n */\nexport function createOperatorSubscriber(\n destination: Subscriber,\n onNext?: (value: T) => void,\n onComplete?: () => void,\n onError?: (err: any) => void,\n onFinalize?: () => void\n): Subscriber {\n return new OperatorSubscriber(destination, onNext, onComplete, onError, onFinalize);\n}\n\n/**\n * A generic helper for allowing operators to be created with a Subscriber and\n * use closures to capture necessary state from the operator function itself.\n */\nexport class OperatorSubscriber extends Subscriber {\n /**\n * Creates an instance of an `OperatorSubscriber`.\n * @param destination The downstream subscriber.\n * @param onNext Handles next values, only called if this subscriber is not stopped or closed. Any\n * error that occurs in this function is caught and sent to the `error` method of this subscriber.\n * @param onError Handles errors from the subscription, any errors that occur in this handler are caught\n * and send to the `destination` error handler.\n * @param onComplete Handles completion notification from the subscription. Any errors that occur in\n * this handler are sent to the `destination` error handler.\n * @param onFinalize Additional finalization logic here. This will only be called on finalization if the\n * subscriber itself is not already closed. This is called after all other finalization logic is executed.\n * @param shouldUnsubscribe An optional check to see if an unsubscribe call should truly unsubscribe.\n * NOTE: This currently **ONLY** exists to support the strange behavior of {@link groupBy}, where unsubscription\n * to the resulting observable does not actually disconnect from the source if there are active subscriptions\n * to any grouped observable. (DO NOT EXPOSE OR USE EXTERNALLY!!!)\n */\n constructor(\n destination: Subscriber,\n onNext?: (value: T) => void,\n onComplete?: () => void,\n onError?: (err: any) => void,\n private onFinalize?: () => void,\n private shouldUnsubscribe?: () => boolean\n ) {\n // It's important - for performance reasons - that all of this class's\n // members are initialized and that they are always initialized in the same\n // order. This will ensure that all OperatorSubscriber instances have the\n // same hidden class in V8. This, in turn, will help keep the number of\n // hidden classes involved in property accesses within the base class as\n // low as possible. If the number of hidden classes involved exceeds four,\n // the property accesses will become megamorphic and performance penalties\n // will be incurred - i.e. inline caches won't be used.\n //\n // The reasons for ensuring all instances have the same hidden class are\n // further discussed in this blog post from Benedikt Meurer:\n // https://benediktmeurer.de/2018/03/23/impact-of-polymorphism-on-component-based-frameworks-like-react/\n super(destination);\n this._next = onNext\n ? function (this: OperatorSubscriber, value: T) {\n try {\n onNext(value);\n } catch (err) {\n destination.error(err);\n }\n }\n : super._next;\n this._error = onError\n ? function (this: OperatorSubscriber, err: any) {\n try {\n onError(err);\n } catch (err) {\n // Send any errors that occur down stream.\n destination.error(err);\n } finally {\n // Ensure finalization.\n this.unsubscribe();\n }\n }\n : super._error;\n this._complete = onComplete\n ? function (this: OperatorSubscriber) {\n try {\n onComplete();\n } catch (err) {\n // Send any errors that occur down stream.\n destination.error(err);\n } finally {\n // Ensure finalization.\n this.unsubscribe();\n }\n }\n : super._complete;\n }\n\n unsubscribe() {\n if (!this.shouldUnsubscribe || this.shouldUnsubscribe()) {\n const { closed } = this;\n super.unsubscribe();\n // Execute additional teardown if we have any and we didn't already do so.\n !closed && this.onFinalize?.();\n }\n }\n}\n", "import { Subscription } from '../Subscription';\n\ninterface AnimationFrameProvider {\n schedule(callback: FrameRequestCallback): Subscription;\n requestAnimationFrame: typeof requestAnimationFrame;\n cancelAnimationFrame: typeof cancelAnimationFrame;\n delegate:\n | {\n requestAnimationFrame: typeof requestAnimationFrame;\n cancelAnimationFrame: typeof cancelAnimationFrame;\n }\n | undefined;\n}\n\nexport const animationFrameProvider: AnimationFrameProvider = {\n // When accessing the delegate, use the variable rather than `this` so that\n // the functions can be called without being bound to the provider.\n schedule(callback) {\n let request = requestAnimationFrame;\n let cancel: typeof cancelAnimationFrame | undefined = cancelAnimationFrame;\n const { delegate } = animationFrameProvider;\n if (delegate) {\n request = delegate.requestAnimationFrame;\n cancel = delegate.cancelAnimationFrame;\n }\n const handle = request((timestamp) => {\n // Clear the cancel function. The request has been fulfilled, so\n // attempting to cancel the request upon unsubscription would be\n // pointless.\n cancel = undefined;\n callback(timestamp);\n });\n return new Subscription(() => cancel?.(handle));\n },\n requestAnimationFrame(...args) {\n const { delegate } = animationFrameProvider;\n return (delegate?.requestAnimationFrame || requestAnimationFrame)(...args);\n },\n cancelAnimationFrame(...args) {\n const { delegate } = animationFrameProvider;\n return (delegate?.cancelAnimationFrame || cancelAnimationFrame)(...args);\n },\n delegate: undefined,\n};\n", "import { createErrorClass } from './createErrorClass';\n\nexport interface ObjectUnsubscribedError extends Error {}\n\nexport interface ObjectUnsubscribedErrorCtor {\n /**\n * @deprecated Internal implementation detail. Do not construct error instances.\n * Cannot be tagged as internal: https://github.com/ReactiveX/rxjs/issues/6269\n */\n new (): ObjectUnsubscribedError;\n}\n\n/**\n * An error thrown when an action is invalid because the object has been\n * unsubscribed.\n *\n * @see {@link Subject}\n * @see {@link BehaviorSubject}\n *\n * @class ObjectUnsubscribedError\n */\nexport const ObjectUnsubscribedError: ObjectUnsubscribedErrorCtor = createErrorClass(\n (_super) =>\n function ObjectUnsubscribedErrorImpl(this: any) {\n _super(this);\n this.name = 'ObjectUnsubscribedError';\n this.message = 'object unsubscribed';\n }\n);\n", "import { Operator } from './Operator';\nimport { Observable } from './Observable';\nimport { Subscriber } from './Subscriber';\nimport { Subscription, EMPTY_SUBSCRIPTION } from './Subscription';\nimport { Observer, SubscriptionLike, TeardownLogic } from './types';\nimport { ObjectUnsubscribedError } from './util/ObjectUnsubscribedError';\nimport { arrRemove } from './util/arrRemove';\nimport { errorContext } from './util/errorContext';\n\n/**\n * A Subject is a special type of Observable that allows values to be\n * multicasted to many Observers. Subjects are like EventEmitters.\n *\n * Every Subject is an Observable and an Observer. You can subscribe to a\n * Subject, and you can call next to feed values as well as error and complete.\n */\nexport class Subject extends Observable implements SubscriptionLike {\n closed = false;\n\n private currentObservers: Observer[] | null = null;\n\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n observers: Observer[] = [];\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n isStopped = false;\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n hasError = false;\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n thrownError: any = null;\n\n /**\n * Creates a \"subject\" by basically gluing an observer to an observable.\n *\n * @deprecated Recommended you do not use. Will be removed at some point in the future. Plans for replacement still under discussion.\n */\n static create: (...args: any[]) => any = (destination: Observer, source: Observable): AnonymousSubject => {\n return new AnonymousSubject(destination, source);\n };\n\n constructor() {\n // NOTE: This must be here to obscure Observable's constructor.\n super();\n }\n\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n lift(operator: Operator): Observable {\n const subject = new AnonymousSubject(this, this);\n subject.operator = operator as any;\n return subject as any;\n }\n\n /** @internal */\n protected _throwIfClosed() {\n if (this.closed) {\n throw new ObjectUnsubscribedError();\n }\n }\n\n next(value: T) {\n errorContext(() => {\n this._throwIfClosed();\n if (!this.isStopped) {\n if (!this.currentObservers) {\n this.currentObservers = Array.from(this.observers);\n }\n for (const observer of this.currentObservers) {\n observer.next(value);\n }\n }\n });\n }\n\n error(err: any) {\n errorContext(() => {\n this._throwIfClosed();\n if (!this.isStopped) {\n this.hasError = this.isStopped = true;\n this.thrownError = err;\n const { observers } = this;\n while (observers.length) {\n observers.shift()!.error(err);\n }\n }\n });\n }\n\n complete() {\n errorContext(() => {\n this._throwIfClosed();\n if (!this.isStopped) {\n this.isStopped = true;\n const { observers } = this;\n while (observers.length) {\n observers.shift()!.complete();\n }\n }\n });\n }\n\n unsubscribe() {\n this.isStopped = this.closed = true;\n this.observers = this.currentObservers = null!;\n }\n\n get observed() {\n return this.observers?.length > 0;\n }\n\n /** @internal */\n protected _trySubscribe(subscriber: Subscriber): TeardownLogic {\n this._throwIfClosed();\n return super._trySubscribe(subscriber);\n }\n\n /** @internal */\n protected _subscribe(subscriber: Subscriber): Subscription {\n this._throwIfClosed();\n this._checkFinalizedStatuses(subscriber);\n return this._innerSubscribe(subscriber);\n }\n\n /** @internal */\n protected _innerSubscribe(subscriber: Subscriber) {\n const { hasError, isStopped, observers } = this;\n if (hasError || isStopped) {\n return EMPTY_SUBSCRIPTION;\n }\n this.currentObservers = null;\n observers.push(subscriber);\n return new Subscription(() => {\n this.currentObservers = null;\n arrRemove(observers, subscriber);\n });\n }\n\n /** @internal */\n protected _checkFinalizedStatuses(subscriber: Subscriber) {\n const { hasError, thrownError, isStopped } = this;\n if (hasError) {\n subscriber.error(thrownError);\n } else if (isStopped) {\n subscriber.complete();\n }\n }\n\n /**\n * Creates a new Observable with this Subject as the source. You can do this\n * to create custom Observer-side logic of the Subject and conceal it from\n * code that uses the Observable.\n * @return Observable that this Subject casts to.\n */\n asObservable(): Observable {\n const observable: any = new Observable();\n observable.source = this;\n return observable;\n }\n}\n\nexport class AnonymousSubject extends Subject {\n constructor(\n /** @deprecated Internal implementation detail, do not use directly. Will be made internal in v8. */\n public destination?: Observer,\n source?: Observable\n ) {\n super();\n this.source = source;\n }\n\n next(value: T) {\n this.destination?.next?.(value);\n }\n\n error(err: any) {\n this.destination?.error?.(err);\n }\n\n complete() {\n this.destination?.complete?.();\n }\n\n /** @internal */\n protected _subscribe(subscriber: Subscriber): Subscription {\n return this.source?.subscribe(subscriber) ?? EMPTY_SUBSCRIPTION;\n }\n}\n", "import { Subject } from './Subject';\nimport { Subscriber } from './Subscriber';\nimport { Subscription } from './Subscription';\n\n/**\n * A variant of Subject that requires an initial value and emits its current\n * value whenever it is subscribed to.\n */\nexport class BehaviorSubject extends Subject {\n constructor(private _value: T) {\n super();\n }\n\n get value(): T {\n return this.getValue();\n }\n\n /** @internal */\n protected _subscribe(subscriber: Subscriber): Subscription {\n const subscription = super._subscribe(subscriber);\n !subscription.closed && subscriber.next(this._value);\n return subscription;\n }\n\n getValue(): T {\n const { hasError, thrownError, _value } = this;\n if (hasError) {\n throw thrownError;\n }\n this._throwIfClosed();\n return _value;\n }\n\n next(value: T): void {\n super.next((this._value = value));\n }\n}\n", "import { TimestampProvider } from '../types';\n\ninterface DateTimestampProvider extends TimestampProvider {\n delegate: TimestampProvider | undefined;\n}\n\nexport const dateTimestampProvider: DateTimestampProvider = {\n now() {\n // Use the variable rather than `this` so that the function can be called\n // without being bound to the provider.\n return (dateTimestampProvider.delegate || Date).now();\n },\n delegate: undefined,\n};\n", "import { Subject } from './Subject';\nimport { TimestampProvider } from './types';\nimport { Subscriber } from './Subscriber';\nimport { Subscription } from './Subscription';\nimport { dateTimestampProvider } from './scheduler/dateTimestampProvider';\n\n/**\n * A variant of {@link Subject} that \"replays\" old values to new subscribers by emitting them when they first subscribe.\n *\n * `ReplaySubject` has an internal buffer that will store a specified number of values that it has observed. Like `Subject`,\n * `ReplaySubject` \"observes\" values by having them passed to its `next` method. When it observes a value, it will store that\n * value for a time determined by the configuration of the `ReplaySubject`, as passed to its constructor.\n *\n * When a new subscriber subscribes to the `ReplaySubject` instance, it will synchronously emit all values in its buffer in\n * a First-In-First-Out (FIFO) manner. The `ReplaySubject` will also complete, if it has observed completion; and it will\n * error if it has observed an error.\n *\n * There are two main configuration items to be concerned with:\n *\n * 1. `bufferSize` - This will determine how many items are stored in the buffer, defaults to infinite.\n * 2. `windowTime` - The amount of time to hold a value in the buffer before removing it from the buffer.\n *\n * Both configurations may exist simultaneously. So if you would like to buffer a maximum of 3 values, as long as the values\n * are less than 2 seconds old, you could do so with a `new ReplaySubject(3, 2000)`.\n *\n * ### Differences with BehaviorSubject\n *\n * `BehaviorSubject` is similar to `new ReplaySubject(1)`, with a couple of exceptions:\n *\n * 1. `BehaviorSubject` comes \"primed\" with a single value upon construction.\n * 2. `ReplaySubject` will replay values, even after observing an error, where `BehaviorSubject` will not.\n *\n * @see {@link Subject}\n * @see {@link BehaviorSubject}\n * @see {@link shareReplay}\n */\nexport class ReplaySubject extends Subject {\n private _buffer: (T | number)[] = [];\n private _infiniteTimeWindow = true;\n\n /**\n * @param _bufferSize The size of the buffer to replay on subscription\n * @param _windowTime The amount of time the buffered items will stay buffered\n * @param _timestampProvider An object with a `now()` method that provides the current timestamp. This is used to\n * calculate the amount of time something has been buffered.\n */\n constructor(\n private _bufferSize = Infinity,\n private _windowTime = Infinity,\n private _timestampProvider: TimestampProvider = dateTimestampProvider\n ) {\n super();\n this._infiniteTimeWindow = _windowTime === Infinity;\n this._bufferSize = Math.max(1, _bufferSize);\n this._windowTime = Math.max(1, _windowTime);\n }\n\n next(value: T): void {\n const { isStopped, _buffer, _infiniteTimeWindow, _timestampProvider, _windowTime } = this;\n if (!isStopped) {\n _buffer.push(value);\n !_infiniteTimeWindow && _buffer.push(_timestampProvider.now() + _windowTime);\n }\n this._trimBuffer();\n super.next(value);\n }\n\n /** @internal */\n protected _subscribe(subscriber: Subscriber): Subscription {\n this._throwIfClosed();\n this._trimBuffer();\n\n const subscription = this._innerSubscribe(subscriber);\n\n const { _infiniteTimeWindow, _buffer } = this;\n // We use a copy here, so reentrant code does not mutate our array while we're\n // emitting it to a new subscriber.\n const copy = _buffer.slice();\n for (let i = 0; i < copy.length && !subscriber.closed; i += _infiniteTimeWindow ? 1 : 2) {\n subscriber.next(copy[i] as T);\n }\n\n this._checkFinalizedStatuses(subscriber);\n\n return subscription;\n }\n\n private _trimBuffer() {\n const { _bufferSize, _timestampProvider, _buffer, _infiniteTimeWindow } = this;\n // If we don't have an infinite buffer size, and we're over the length,\n // use splice to truncate the old buffer values off. Note that we have to\n // double the size for instances where we're not using an infinite time window\n // because we're storing the values and the timestamps in the same array.\n const adjustedBufferSize = (_infiniteTimeWindow ? 1 : 2) * _bufferSize;\n _bufferSize < Infinity && adjustedBufferSize < _buffer.length && _buffer.splice(0, _buffer.length - adjustedBufferSize);\n\n // Now, if we're not in an infinite time window, remove all values where the time is\n // older than what is allowed.\n if (!_infiniteTimeWindow) {\n const now = _timestampProvider.now();\n let last = 0;\n // Search the array for the first timestamp that isn't expired and\n // truncate the buffer up to that point.\n for (let i = 1; i < _buffer.length && (_buffer[i] as number) <= now; i += 2) {\n last = i;\n }\n last && _buffer.splice(0, last + 1);\n }\n }\n}\n", "import { Scheduler } from '../Scheduler';\nimport { Subscription } from '../Subscription';\nimport { SchedulerAction } from '../types';\n\n/**\n * A unit of work to be executed in a `scheduler`. An action is typically\n * created from within a {@link SchedulerLike} and an RxJS user does not need to concern\n * themselves about creating and manipulating an Action.\n *\n * ```ts\n * class Action extends Subscription {\n * new (scheduler: Scheduler, work: (state?: T) => void);\n * schedule(state?: T, delay: number = 0): Subscription;\n * }\n * ```\n */\nexport class Action extends Subscription {\n constructor(scheduler: Scheduler, work: (this: SchedulerAction, state?: T) => void) {\n super();\n }\n /**\n * Schedules this action on its parent {@link SchedulerLike} for execution. May be passed\n * some context object, `state`. May happen at some point in the future,\n * according to the `delay` parameter, if specified.\n * @param state Some contextual data that the `work` function uses when called by the\n * Scheduler.\n * @param delay Time to wait before executing the work, where the time unit is implicit\n * and defined by the Scheduler.\n * @return A subscription in order to be able to unsubscribe the scheduled work.\n */\n public schedule(state?: T, delay: number = 0): Subscription {\n return this;\n }\n}\n", "import type { TimerHandle } from './timerHandle';\ntype SetIntervalFunction = (handler: () => void, timeout?: number, ...args: any[]) => TimerHandle;\ntype ClearIntervalFunction = (handle: TimerHandle) => void;\n\ninterface IntervalProvider {\n setInterval: SetIntervalFunction;\n clearInterval: ClearIntervalFunction;\n delegate:\n | {\n setInterval: SetIntervalFunction;\n clearInterval: ClearIntervalFunction;\n }\n | undefined;\n}\n\nexport const intervalProvider: IntervalProvider = {\n // When accessing the delegate, use the variable rather than `this` so that\n // the functions can be called without being bound to the provider.\n setInterval(handler: () => void, timeout?: number, ...args) {\n const { delegate } = intervalProvider;\n if (delegate?.setInterval) {\n return delegate.setInterval(handler, timeout, ...args);\n }\n return setInterval(handler, timeout, ...args);\n },\n clearInterval(handle) {\n const { delegate } = intervalProvider;\n return (delegate?.clearInterval || clearInterval)(handle as any);\n },\n delegate: undefined,\n};\n", "import { Action } from './Action';\nimport { SchedulerAction } from '../types';\nimport { Subscription } from '../Subscription';\nimport { AsyncScheduler } from './AsyncScheduler';\nimport { intervalProvider } from './intervalProvider';\nimport { arrRemove } from '../util/arrRemove';\nimport { TimerHandle } from './timerHandle';\n\nexport class AsyncAction extends Action {\n public id: TimerHandle | undefined;\n public state?: T;\n // @ts-ignore: Property has no initializer and is not definitely assigned\n public delay: number;\n protected pending: boolean = false;\n\n constructor(protected scheduler: AsyncScheduler, protected work: (this: SchedulerAction, state?: T) => void) {\n super(scheduler, work);\n }\n\n public schedule(state?: T, delay: number = 0): Subscription {\n if (this.closed) {\n return this;\n }\n\n // Always replace the current state with the new state.\n this.state = state;\n\n const id = this.id;\n const scheduler = this.scheduler;\n\n //\n // Important implementation note:\n //\n // Actions only execute once by default, unless rescheduled from within the\n // scheduled callback. This allows us to implement single and repeat\n // actions via the same code path, without adding API surface area, as well\n // as mimic traditional recursion but across asynchronous boundaries.\n //\n // However, JS runtimes and timers distinguish between intervals achieved by\n // serial `setTimeout` calls vs. a single `setInterval` call. An interval of\n // serial `setTimeout` calls can be individually delayed, which delays\n // scheduling the next `setTimeout`, and so on. `setInterval` attempts to\n // guarantee the interval callback will be invoked more precisely to the\n // interval period, regardless of load.\n //\n // Therefore, we use `setInterval` to schedule single and repeat actions.\n // If the action reschedules itself with the same delay, the interval is not\n // canceled. If the action doesn't reschedule, or reschedules with a\n // different delay, the interval will be canceled after scheduled callback\n // execution.\n //\n if (id != null) {\n this.id = this.recycleAsyncId(scheduler, id, delay);\n }\n\n // Set the pending flag indicating that this action has been scheduled, or\n // has recursively rescheduled itself.\n this.pending = true;\n\n this.delay = delay;\n // If this action has already an async Id, don't request a new one.\n this.id = this.id ?? this.requestAsyncId(scheduler, this.id, delay);\n\n return this;\n }\n\n protected requestAsyncId(scheduler: AsyncScheduler, _id?: TimerHandle, delay: number = 0): TimerHandle {\n return intervalProvider.setInterval(scheduler.flush.bind(scheduler, this), delay);\n }\n\n protected recycleAsyncId(_scheduler: AsyncScheduler, id?: TimerHandle, delay: number | null = 0): TimerHandle | undefined {\n // If this action is rescheduled with the same delay time, don't clear the interval id.\n if (delay != null && this.delay === delay && this.pending === false) {\n return id;\n }\n // Otherwise, if the action's delay time is different from the current delay,\n // or the action has been rescheduled before it's executed, clear the interval id\n if (id != null) {\n intervalProvider.clearInterval(id);\n }\n\n return undefined;\n }\n\n /**\n * Immediately executes this action and the `work` it contains.\n */\n public execute(state: T, delay: number): any {\n if (this.closed) {\n return new Error('executing a cancelled action');\n }\n\n this.pending = false;\n const error = this._execute(state, delay);\n if (error) {\n return error;\n } else if (this.pending === false && this.id != null) {\n // Dequeue if the action didn't reschedule itself. Don't call\n // unsubscribe(), because the action could reschedule later.\n // For example:\n // ```\n // scheduler.schedule(function doWork(counter) {\n // /* ... I'm a busy worker bee ... */\n // var originalAction = this;\n // /* wait 100ms before rescheduling the action */\n // setTimeout(function () {\n // originalAction.schedule(counter + 1);\n // }, 100);\n // }, 1000);\n // ```\n this.id = this.recycleAsyncId(this.scheduler, this.id, null);\n }\n }\n\n protected _execute(state: T, _delay: number): any {\n let errored: boolean = false;\n let errorValue: any;\n try {\n this.work(state);\n } catch (e) {\n errored = true;\n // HACK: Since code elsewhere is relying on the \"truthiness\" of the\n // return here, we can't have it return \"\" or 0 or false.\n // TODO: Clean this up when we refactor schedulers mid-version-8 or so.\n errorValue = e ? e : new Error('Scheduled action threw falsy error');\n }\n if (errored) {\n this.unsubscribe();\n return errorValue;\n }\n }\n\n unsubscribe() {\n if (!this.closed) {\n const { id, scheduler } = this;\n const { actions } = scheduler;\n\n this.work = this.state = this.scheduler = null!;\n this.pending = false;\n\n arrRemove(actions, this);\n if (id != null) {\n this.id = this.recycleAsyncId(scheduler, id, null);\n }\n\n this.delay = null!;\n super.unsubscribe();\n }\n }\n}\n", "import { Action } from './scheduler/Action';\nimport { Subscription } from './Subscription';\nimport { SchedulerLike, SchedulerAction } from './types';\nimport { dateTimestampProvider } from './scheduler/dateTimestampProvider';\n\n/**\n * An execution context and a data structure to order tasks and schedule their\n * execution. Provides a notion of (potentially virtual) time, through the\n * `now()` getter method.\n *\n * Each unit of work in a Scheduler is called an `Action`.\n *\n * ```ts\n * class Scheduler {\n * now(): number;\n * schedule(work, delay?, state?): Subscription;\n * }\n * ```\n *\n * @deprecated Scheduler is an internal implementation detail of RxJS, and\n * should not be used directly. Rather, create your own class and implement\n * {@link SchedulerLike}. Will be made internal in v8.\n */\nexport class Scheduler implements SchedulerLike {\n public static now: () => number = dateTimestampProvider.now;\n\n constructor(private schedulerActionCtor: typeof Action, now: () => number = Scheduler.now) {\n this.now = now;\n }\n\n /**\n * A getter method that returns a number representing the current time\n * (at the time this function was called) according to the scheduler's own\n * internal clock.\n * @return A number that represents the current time. May or may not\n * have a relation to wall-clock time. May or may not refer to a time unit\n * (e.g. milliseconds).\n */\n public now: () => number;\n\n /**\n * Schedules a function, `work`, for execution. May happen at some point in\n * the future, according to the `delay` parameter, if specified. May be passed\n * some context object, `state`, which will be passed to the `work` function.\n *\n * The given arguments will be processed an stored as an Action object in a\n * queue of actions.\n *\n * @param work A function representing a task, or some unit of work to be\n * executed by the Scheduler.\n * @param delay Time to wait before executing the work, where the time unit is\n * implicit and defined by the Scheduler itself.\n * @param state Some contextual data that the `work` function uses when called\n * by the Scheduler.\n * @return A subscription in order to be able to unsubscribe the scheduled work.\n */\n public schedule(work: (this: SchedulerAction, state?: T) => void, delay: number = 0, state?: T): Subscription {\n return new this.schedulerActionCtor(this, work).schedule(state, delay);\n }\n}\n", "import { Scheduler } from '../Scheduler';\nimport { Action } from './Action';\nimport { AsyncAction } from './AsyncAction';\nimport { TimerHandle } from './timerHandle';\n\nexport class AsyncScheduler extends Scheduler {\n public actions: Array> = [];\n /**\n * A flag to indicate whether the Scheduler is currently executing a batch of\n * queued actions.\n * @internal\n */\n public _active: boolean = false;\n /**\n * An internal ID used to track the latest asynchronous task such as those\n * coming from `setTimeout`, `setInterval`, `requestAnimationFrame`, and\n * others.\n * @internal\n */\n public _scheduled: TimerHandle | undefined;\n\n constructor(SchedulerAction: typeof Action, now: () => number = Scheduler.now) {\n super(SchedulerAction, now);\n }\n\n public flush(action: AsyncAction): void {\n const { actions } = this;\n\n if (this._active) {\n actions.push(action);\n return;\n }\n\n let error: any;\n this._active = true;\n\n do {\n if ((error = action.execute(action.state, action.delay))) {\n break;\n }\n } while ((action = actions.shift()!)); // exhaust the scheduler queue\n\n this._active = false;\n\n if (error) {\n while ((action = actions.shift()!)) {\n action.unsubscribe();\n }\n throw error;\n }\n }\n}\n", "import { AsyncAction } from './AsyncAction';\nimport { AsyncScheduler } from './AsyncScheduler';\n\n/**\n *\n * Async Scheduler\n *\n * Schedule task as if you used setTimeout(task, duration)\n *\n * `async` scheduler schedules tasks asynchronously, by putting them on the JavaScript\n * event loop queue. It is best used to delay tasks in time or to schedule tasks repeating\n * in intervals.\n *\n * If you just want to \"defer\" task, that is to perform it right after currently\n * executing synchronous code ends (commonly achieved by `setTimeout(deferredTask, 0)`),\n * better choice will be the {@link asapScheduler} scheduler.\n *\n * ## Examples\n * Use async scheduler to delay task\n * ```ts\n * import { asyncScheduler } from 'rxjs';\n *\n * const task = () => console.log('it works!');\n *\n * asyncScheduler.schedule(task, 2000);\n *\n * // After 2 seconds logs:\n * // \"it works!\"\n * ```\n *\n * Use async scheduler to repeat task in intervals\n * ```ts\n * import { asyncScheduler } from 'rxjs';\n *\n * function task(state) {\n * console.log(state);\n * this.schedule(state + 1, 1000); // `this` references currently executing Action,\n * // which we reschedule with new state and delay\n * }\n *\n * asyncScheduler.schedule(task, 3000, 0);\n *\n * // Logs:\n * // 0 after 3s\n * // 1 after 4s\n * // 2 after 5s\n * // 3 after 6s\n * ```\n */\n\nexport const asyncScheduler = new AsyncScheduler(AsyncAction);\n\n/**\n * @deprecated Renamed to {@link asyncScheduler}. Will be removed in v8.\n */\nexport const async = asyncScheduler;\n", "import { AsyncAction } from './AsyncAction';\nimport { Subscription } from '../Subscription';\nimport { QueueScheduler } from './QueueScheduler';\nimport { SchedulerAction } from '../types';\nimport { TimerHandle } from './timerHandle';\n\nexport class QueueAction extends AsyncAction {\n constructor(protected scheduler: QueueScheduler, protected work: (this: SchedulerAction, state?: T) => void) {\n super(scheduler, work);\n }\n\n public schedule(state?: T, delay: number = 0): Subscription {\n if (delay > 0) {\n return super.schedule(state, delay);\n }\n this.delay = delay;\n this.state = state;\n this.scheduler.flush(this);\n return this;\n }\n\n public execute(state: T, delay: number): any {\n return delay > 0 || this.closed ? super.execute(state, delay) : this._execute(state, delay);\n }\n\n protected requestAsyncId(scheduler: QueueScheduler, id?: TimerHandle, delay: number = 0): TimerHandle {\n // If delay exists and is greater than 0, or if the delay is null (the\n // action wasn't rescheduled) but was originally scheduled as an async\n // action, then recycle as an async action.\n\n if ((delay != null && delay > 0) || (delay == null && this.delay > 0)) {\n return super.requestAsyncId(scheduler, id, delay);\n }\n\n // Otherwise flush the scheduler starting with this action.\n scheduler.flush(this);\n\n // HACK: In the past, this was returning `void`. However, `void` isn't a valid\n // `TimerHandle`, and generally the return value here isn't really used. So the\n // compromise is to return `0` which is both \"falsy\" and a valid `TimerHandle`,\n // as opposed to refactoring every other instanceo of `requestAsyncId`.\n return 0;\n }\n}\n", "import { AsyncScheduler } from './AsyncScheduler';\n\nexport class QueueScheduler extends AsyncScheduler {\n}\n", "import { QueueAction } from './QueueAction';\nimport { QueueScheduler } from './QueueScheduler';\n\n/**\n *\n * Queue Scheduler\n *\n * Put every next task on a queue, instead of executing it immediately\n *\n * `queue` scheduler, when used with delay, behaves the same as {@link asyncScheduler} scheduler.\n *\n * When used without delay, it schedules given task synchronously - executes it right when\n * it is scheduled. However when called recursively, that is when inside the scheduled task,\n * another task is scheduled with queue scheduler, instead of executing immediately as well,\n * that task will be put on a queue and wait for current one to finish.\n *\n * This means that when you execute task with `queue` scheduler, you are sure it will end\n * before any other task scheduled with that scheduler will start.\n *\n * ## Examples\n * Schedule recursively first, then do something\n * ```ts\n * import { queueScheduler } from 'rxjs';\n *\n * queueScheduler.schedule(() => {\n * queueScheduler.schedule(() => console.log('second')); // will not happen now, but will be put on a queue\n *\n * console.log('first');\n * });\n *\n * // Logs:\n * // \"first\"\n * // \"second\"\n * ```\n *\n * Reschedule itself recursively\n * ```ts\n * import { queueScheduler } from 'rxjs';\n *\n * queueScheduler.schedule(function(state) {\n * if (state !== 0) {\n * console.log('before', state);\n * this.schedule(state - 1); // `this` references currently executing Action,\n * // which we reschedule with new state\n * console.log('after', state);\n * }\n * }, 0, 3);\n *\n * // In scheduler that runs recursively, you would expect:\n * // \"before\", 3\n * // \"before\", 2\n * // \"before\", 1\n * // \"after\", 1\n * // \"after\", 2\n * // \"after\", 3\n *\n * // But with queue it logs:\n * // \"before\", 3\n * // \"after\", 3\n * // \"before\", 2\n * // \"after\", 2\n * // \"before\", 1\n * // \"after\", 1\n * ```\n */\n\nexport const queueScheduler = new QueueScheduler(QueueAction);\n\n/**\n * @deprecated Renamed to {@link queueScheduler}. Will be removed in v8.\n */\nexport const queue = queueScheduler;\n", "import { AsyncAction } from './AsyncAction';\nimport { AnimationFrameScheduler } from './AnimationFrameScheduler';\nimport { SchedulerAction } from '../types';\nimport { animationFrameProvider } from './animationFrameProvider';\nimport { TimerHandle } from './timerHandle';\n\nexport class AnimationFrameAction extends AsyncAction {\n constructor(protected scheduler: AnimationFrameScheduler, protected work: (this: SchedulerAction, state?: T) => void) {\n super(scheduler, work);\n }\n\n protected requestAsyncId(scheduler: AnimationFrameScheduler, id?: TimerHandle, delay: number = 0): TimerHandle {\n // If delay is greater than 0, request as an async action.\n if (delay !== null && delay > 0) {\n return super.requestAsyncId(scheduler, id, delay);\n }\n // Push the action to the end of the scheduler queue.\n scheduler.actions.push(this);\n // If an animation frame has already been requested, don't request another\n // one. If an animation frame hasn't been requested yet, request one. Return\n // the current animation frame request id.\n return scheduler._scheduled || (scheduler._scheduled = animationFrameProvider.requestAnimationFrame(() => scheduler.flush(undefined)));\n }\n\n protected recycleAsyncId(scheduler: AnimationFrameScheduler, id?: TimerHandle, delay: number = 0): TimerHandle | undefined {\n // If delay exists and is greater than 0, or if the delay is null (the\n // action wasn't rescheduled) but was originally scheduled as an async\n // action, then recycle as an async action.\n if (delay != null ? delay > 0 : this.delay > 0) {\n return super.recycleAsyncId(scheduler, id, delay);\n }\n // If the scheduler queue has no remaining actions with the same async id,\n // cancel the requested animation frame and set the scheduled flag to\n // undefined so the next AnimationFrameAction will request its own.\n const { actions } = scheduler;\n if (id != null && id === scheduler._scheduled && actions[actions.length - 1]?.id !== id) {\n animationFrameProvider.cancelAnimationFrame(id as number);\n scheduler._scheduled = undefined;\n }\n // Return undefined so the action knows to request a new async id if it's rescheduled.\n return undefined;\n }\n}\n", "import { AsyncAction } from './AsyncAction';\nimport { AsyncScheduler } from './AsyncScheduler';\n\nexport class AnimationFrameScheduler extends AsyncScheduler {\n public flush(action?: AsyncAction): void {\n this._active = true;\n // The async id that effects a call to flush is stored in _scheduled.\n // Before executing an action, it's necessary to check the action's async\n // id to determine whether it's supposed to be executed in the current\n // flush.\n // Previous implementations of this method used a count to determine this,\n // but that was unsound, as actions that are unsubscribed - i.e. cancelled -\n // are removed from the actions array and that can shift actions that are\n // scheduled to be executed in a subsequent flush into positions at which\n // they are executed within the current flush.\n let flushId;\n if (action) {\n flushId = action.id;\n } else {\n flushId = this._scheduled;\n this._scheduled = undefined;\n }\n\n const { actions } = this;\n let error: any;\n action = action || actions.shift()!;\n\n do {\n if ((error = action.execute(action.state, action.delay))) {\n break;\n }\n } while ((action = actions[0]) && action.id === flushId && actions.shift());\n\n this._active = false;\n\n if (error) {\n while ((action = actions[0]) && action.id === flushId && actions.shift()) {\n action.unsubscribe();\n }\n throw error;\n }\n }\n}\n", "import { AnimationFrameAction } from './AnimationFrameAction';\nimport { AnimationFrameScheduler } from './AnimationFrameScheduler';\n\n/**\n *\n * Animation Frame Scheduler\n *\n * Perform task when `window.requestAnimationFrame` would fire\n *\n * When `animationFrame` scheduler is used with delay, it will fall back to {@link asyncScheduler} scheduler\n * behaviour.\n *\n * Without delay, `animationFrame` scheduler can be used to create smooth browser animations.\n * It makes sure scheduled task will happen just before next browser content repaint,\n * thus performing animations as efficiently as possible.\n *\n * ## Example\n * Schedule div height animation\n * ```ts\n * // html:
\n * import { animationFrameScheduler } from 'rxjs';\n *\n * const div = document.querySelector('div');\n *\n * animationFrameScheduler.schedule(function(height) {\n * div.style.height = height + \"px\";\n *\n * this.schedule(height + 1); // `this` references currently executing Action,\n * // which we reschedule with new state\n * }, 0, 0);\n *\n * // You will see a div element growing in height\n * ```\n */\n\nexport const animationFrameScheduler = new AnimationFrameScheduler(AnimationFrameAction);\n\n/**\n * @deprecated Renamed to {@link animationFrameScheduler}. Will be removed in v8.\n */\nexport const animationFrame = animationFrameScheduler;\n", "import { Observable } from '../Observable';\nimport { SchedulerLike } from '../types';\n\n/**\n * A simple Observable that emits no items to the Observer and immediately\n * emits a complete notification.\n *\n * Just emits 'complete', and nothing else.\n *\n * ![](empty.png)\n *\n * A simple Observable that only emits the complete notification. It can be used\n * for composing with other Observables, such as in a {@link mergeMap}.\n *\n * ## Examples\n *\n * Log complete notification\n *\n * ```ts\n * import { EMPTY } from 'rxjs';\n *\n * EMPTY.subscribe({\n * next: () => console.log('Next'),\n * complete: () => console.log('Complete!')\n * });\n *\n * // Outputs\n * // Complete!\n * ```\n *\n * Emit the number 7, then complete\n *\n * ```ts\n * import { EMPTY, startWith } from 'rxjs';\n *\n * const result = EMPTY.pipe(startWith(7));\n * result.subscribe(x => console.log(x));\n *\n * // Outputs\n * // 7\n * ```\n *\n * Map and flatten only odd numbers to the sequence `'a'`, `'b'`, `'c'`\n *\n * ```ts\n * import { interval, mergeMap, of, EMPTY } from 'rxjs';\n *\n * const interval$ = interval(1000);\n * const result = interval$.pipe(\n * mergeMap(x => x % 2 === 1 ? of('a', 'b', 'c') : EMPTY),\n * );\n * result.subscribe(x => console.log(x));\n *\n * // Results in the following to the console:\n * // x is equal to the count on the interval, e.g. (0, 1, 2, 3, ...)\n * // x will occur every 1000ms\n * // if x % 2 is equal to 1, print a, b, c (each on its own)\n * // if x % 2 is not equal to 1, nothing will be output\n * ```\n *\n * @see {@link Observable}\n * @see {@link NEVER}\n * @see {@link of}\n * @see {@link throwError}\n */\nexport const EMPTY = new Observable((subscriber) => subscriber.complete());\n\n/**\n * @param scheduler A {@link SchedulerLike} to use for scheduling\n * the emission of the complete notification.\n * @deprecated Replaced with the {@link EMPTY} constant or {@link scheduled} (e.g. `scheduled([], scheduler)`). Will be removed in v8.\n */\nexport function empty(scheduler?: SchedulerLike) {\n return scheduler ? emptyScheduled(scheduler) : EMPTY;\n}\n\nfunction emptyScheduled(scheduler: SchedulerLike) {\n return new Observable((subscriber) => scheduler.schedule(() => subscriber.complete()));\n}\n", "import { SchedulerLike } from '../types';\nimport { isFunction } from './isFunction';\n\nexport function isScheduler(value: any): value is SchedulerLike {\n return value && isFunction(value.schedule);\n}\n", "import { SchedulerLike } from '../types';\nimport { isFunction } from './isFunction';\nimport { isScheduler } from './isScheduler';\n\nfunction last(arr: T[]): T | undefined {\n return arr[arr.length - 1];\n}\n\nexport function popResultSelector(args: any[]): ((...args: unknown[]) => unknown) | undefined {\n return isFunction(last(args)) ? args.pop() : undefined;\n}\n\nexport function popScheduler(args: any[]): SchedulerLike | undefined {\n return isScheduler(last(args)) ? args.pop() : undefined;\n}\n\nexport function popNumber(args: any[], defaultValue: number): number {\n return typeof last(args) === 'number' ? args.pop()! : defaultValue;\n}\n", "export const isArrayLike = ((x: any): x is ArrayLike => x && typeof x.length === 'number' && typeof x !== 'function');", "import { isFunction } from \"./isFunction\";\n\n/**\n * Tests to see if the object is \"thennable\".\n * @param value the object to test\n */\nexport function isPromise(value: any): value is PromiseLike {\n return isFunction(value?.then);\n}\n", "import { InteropObservable } from '../types';\nimport { observable as Symbol_observable } from '../symbol/observable';\nimport { isFunction } from './isFunction';\n\n/** Identifies an input as being Observable (but not necessary an Rx Observable) */\nexport function isInteropObservable(input: any): input is InteropObservable {\n return isFunction(input[Symbol_observable]);\n}\n", "import { isFunction } from './isFunction';\n\nexport function isAsyncIterable(obj: any): obj is AsyncIterable {\n return Symbol.asyncIterator && isFunction(obj?.[Symbol.asyncIterator]);\n}\n", "/**\n * Creates the TypeError to throw if an invalid object is passed to `from` or `scheduled`.\n * @param input The object that was passed.\n */\nexport function createInvalidObservableTypeError(input: any) {\n // TODO: We should create error codes that can be looked up, so this can be less verbose.\n return new TypeError(\n `You provided ${\n input !== null && typeof input === 'object' ? 'an invalid object' : `'${input}'`\n } where a stream was expected. You can provide an Observable, Promise, ReadableStream, Array, AsyncIterable, or Iterable.`\n );\n}\n", "export function getSymbolIterator(): symbol {\n if (typeof Symbol !== 'function' || !Symbol.iterator) {\n return '@@iterator' as any;\n }\n\n return Symbol.iterator;\n}\n\nexport const iterator = getSymbolIterator();\n", "import { iterator as Symbol_iterator } from '../symbol/iterator';\nimport { isFunction } from './isFunction';\n\n/** Identifies an input as being an Iterable */\nexport function isIterable(input: any): input is Iterable {\n return isFunction(input?.[Symbol_iterator]);\n}\n", "import { ReadableStreamLike } from '../types';\nimport { isFunction } from './isFunction';\n\nexport async function* readableStreamLikeToAsyncGenerator(readableStream: ReadableStreamLike): AsyncGenerator {\n const reader = readableStream.getReader();\n try {\n while (true) {\n const { value, done } = await reader.read();\n if (done) {\n return;\n }\n yield value!;\n }\n } finally {\n reader.releaseLock();\n }\n}\n\nexport function isReadableStreamLike(obj: any): obj is ReadableStreamLike {\n // We don't want to use instanceof checks because they would return\n // false for instances from another Realm, like an