SRP 实现 Cook-Torrance BRDF

news2024/12/26 0:07:07

写的很乱!

BRDF(Bidirectional Reflectance Distribution Function)全称双向反射分布函数。辐射量单位非常多,这里为方便直观理解,会用非常不严谨的光照强度来解释说明。

BRDF光照模型,上反射率公式

\int_{\Omega }^{}f(p,w_{i},w_{o})L_{i}(p,w_{i})cos\theta dw_{i}

计算的是在法线所在半球上的所有入射光线,在出射方向光强总和,公式有俩个部分。

f(p,w_{i},w_{o})

即BRDF,从特定的入射方向到出射方向,在某个表面上,光强的衰减比率。

L_{i}(p,w_{i})cos \theta _{i}

有了比率,还需要知道在物体表面接收到的某个方向上的光强,所以前半部分是光线沿入射方向到p点发射的光线强度,后半部分用表面与入射方向的夹角计算实际接收到的光线强度(因为这里虽然是一个点,但同时也需要当做一个极小的平面区域处理)。

关于几何函数

为什么GXX有俩个?只考虑一次反射能成功的,其实就是简单的认为入射方向能不被遮挡的光线比例 × 出射方向能不被遮挡的光线比例 = 整条光路上不会被遮挡的光线比例。

Cook-Torrance BRDF

🔻如果在渲染结果中发现了类似以下的问题或变,极大概率是法线没有在片元着色器中标准化。

SRP中实现的Cook-Torrance BRDF效果(无法线贴图,未进行gamma调整):

增加支持多光源

有很多免费的材质素材:Hundreds of 3D Texture Downloads - Free PBR Materials

这部分是没什么难度的,将相关变量设置为数组即可。

🔻但问题就出现在这,在Unity 2022.3.45f1c1中,如果先实现了.SetGlobalVector然后修改成.SetGlobalVectorArray,那么恭喜你完犊子了。修改后传入GPU的数据还是按.SetGlobalVector处理的,Dubug半天你会发现GPU就是拿不到完整的数组。

解决方法:Unity!重启!😎

🔻如果遇到CullingResults.lightIndexCount在某些视角下会返回非常奇怪的(错误)值的情况,例如明明只有2个可见光源,但返回4个。用CullingResults.visibleLights.Length来获取可见光源数组的个数,而不是CullingResults.lightIndexCount即可。

🔻Shader error in 'Custom/BRDF': Unexpected directive '';这种错多半是用了中文分号或者其他的全半角问题。

支持法线贴图,如果想要在切线空间引用法线贴图后再转到世界坐标系下,需要用TBN矩阵的逆矩阵实现。具体见:TBN矩阵的使用

🔻应用贴图实现的BRDF效果很抽象,我以为是TNB出问题了(甚至怀疑unity默认切线有问题,还从blender中导了个正确的球模型过来),整了很久发现是采样法线贴图出错了,虽然各种设置了Unity项目是线性空间,但纹理这一块疑似过于独立了,需要在贴图中应用为Normal Map才能确保纹理采样在线性空间下。

那么有朋友就要问了,unity!unity!我用Default但不勾选sRGB不就可以了吗?unity答:不行。

为什么呢?因为根据TNB矩阵的组织方式,切线空间中垂直表面的轴实际上是x轴而不是z轴,如果想直接使用原始法线贴图,需要调整颜色通道顺序。

如果只用线性空间采样,输出采样结果经过映射后其实是大红色的,即(1.0,0.0,0.0),怎么回事呢?

 映射指颜色范围0~1到法线范围-1~1,而映射前是粉红色的。据说解包出的法线纹理也是粉红色的,和这个有关系吗?

法线贴图And图像压缩

贴图设置为Normal Map就可以了吗?换个法线变换明显材质试试看看😕。

输出法线,很明显少了一部分。

怎么回事呢,采访一下纹理压缩格式。

DXT5|BC3:“我不到啊。”

Unity:“他说(DXT5|BC3)他的alpha和green通道最好用,我就按他说的来了。”

言归正传,BC3是DXT5在DirectX 10中的别称,以下都以将以BC3称呼,BC是怎么压缩图像的这里就不过多解释了,直接看BC3压缩后的格式。

BC3 格式使用 5:6:5 颜色(5 位红色、6 位绿色、5 位蓝色)存储颜色数据,使用一个字节存储 alpha 数据。 更多的位数意味着更大的颜色深度,即更好的颜色表现。对法线来说,其归一化后长度为1,知道x,y即可计算z,所以当选择Texture为NormalMap时,Unity会自动处理成alpha和green通道以帮助我们得到最佳的图像效果。

在压缩大多数 RGBA 纹理时最好使用此格式。对于 RGB(无 Alpha)纹理,DXT1 更适合。当针对 DX11 类硬件(新版 PC、PS4、XboxOne)时,使用 BC7 可能很有用,因为压缩品质通常更好。

拓展一下,Unity中RGBA默认用BC3或BC7,BC7有八种模式,但RGBA支持模式下位数最多(之一)的还是alpha通道,颜色三通道则相同,所以用alpha加green通道也是最优解。

虽然法线贴图是RGB图,但还是用了RGBA的处理方式。

此外,Unity法线贴图也支持用BC5进行压缩,不过对应的采样代码就要修改了,反正怎么压缩怎么采样即可。

所以如果想得到正确的法线贴图采样结果,就需要手动提取alpha和green通道作为yz,再求解出x,这里的对应关系很重要。

通过UV检查图确定UV坐标原点为面左下角,然后只输出法线贴图上采样green通道的颜色。

可以确定green通道对应副切线,输出alpha通道。

可知alpha通道对应切线,那么法线方向的值就是要算的了,计算出法线方向输出值输出结果如下。

说明前面的推测是正确的,又由于TNB矩阵的组织方式是world normal,world tangent,world bitangent,所以alpha和green通道作为yz,再求解出最后一个值作为x。

float3x3 TNB = float3x3(normalize(v2p.normal), normalize(v2p.tangent), normalize(v2p.bitangent));

处理一下就能够将原法线贴图绘制出来,右图颜色更深是由于右图在线性颜色空间下+显示屏Gamma2.2调整,左图在线性空间下+Unity自动Gamma0.45调整+显示屏Gamma2.2调整。

修正后效果:

🔻Gamma问题,光照对了但球的颜色很淡很淡。原因是Color Space选择Linear时,Unity会自动对渲染窗口做一次Gamma0.45。而我参考LearnOpen GL在Shader中也实现了一次Gamma 0.45,所以结果是两次Gamma0.45,加上显示器的一次Gamma2.2,最终输出颜色还是Gamma 0.45,所以会感觉很淡(如果Color Space选Gamma,就需要手动校正)。

参考:Gamma 和 Linear Space的设置 Unity

然后终终终终于实现成功了“网图”的效果!

Unity中显示法线,切线,副切线(附代码)

先前为了查看切线是否有问题,写了个非常简单的脚本显示各顶点的切线坐标轴(使用前要reset对象的transform),结果显示模型非常健康。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ShowNormalsAndTangents : MonoBehaviour
{
    [SerializeField]
    float normalLength = 0.03f;

    // Update is called once per frame
    void OnDrawGizmosSelected()
    {
        Mesh mesh = GetComponent<MeshFilter>().sharedMesh;
        Vector3[] v = mesh.vertices;
        Vector3[] n = mesh.normals;
        Vector4[] t = mesh.tangents;
        for (int i = 0; i < v.Length; i++) {
            Gizmos.color = Color.red;
            Gizmos.DrawRay(v[i], n[i] * normalLength);

            Gizmos.color = Color.green;
            Gizmos.DrawRay(v[i], new Vector3(t[i].x, t[i].y, t[i].z) * normalLength);

            Vector3 bitangent = Vector3.Normalize(Vector3.Cross(n[i], new Vector3(t[i].x, t[i].y, t[i].z)) * t[i].w);
            Gizmos.color = Color.blue;
            Gizmos.DrawRay(v[i], bitangent * normalLength);
        }
    }
}

Unity中显示法线,切线,副切线(几何着色器实现代码)

这里再补充一个用几何着色器实现的法线,切线和副切线显示。完全重叠的线只会显示一条,所以有可能会看到缺少了某条线,其实是被覆盖掉了。

// Upgrade NOTE: replaced '_World2Object' with 'unity_WorldToObject'
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'
 
// Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld'
// Upgrade NOTE: replaced '_World2Object' with 'unity_WorldToObject'
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'
 
Shader "Custom/ShowNormals"
{
    Properties
    {
        _LineLength("LineLength", float) = 0.03
        _NormalLineColor("NormalLineColor", Color) = (1.0, 0.0, 0.0, 1.0)
        _TangentLineColor("TangentLineColor", Color) = (0.0, 1.0, 0.0, 1.0)
        _BitangentLineColor("BitangentLineColor", Color) = (0.0, 0.0, 1.0, 1.0)
    }
    SubShader
    {
        Pass
        {
 
            HLSLPROGRAM
            #pragma vertex VS_Main
            #pragma fragment FS_Main
            #pragma geometry GS_Main
 
            float4x4 unity_MatrixVP;
            float4x4 unity_ObjectToWorld;
            float4x4 unity_WorldToObject;

            float _LineLength;
            float4 _NormalLineColor;
            float4 _TangentLineColor;
            float4 _BitangentLineColor;

                 
            struct VS_INPUT{
                float3 m_pos:POSITION;
                float3 normal:NORMAL;
                float4 tangent:TANGENT;
            };
 
            struct GS_INPUT
            {
                float4 pos:POSITION;
                float3 w_normal:NORMAL;
                float3 w_tangent:TANGENT;
                float3 w_bitangent:TEXCOORD0;
            };
            struct FS_INPUT
            {
                float4 pos:POSITION;
                float4 lineColor:TEXCOORD0;
            };
            //step1
            GS_INPUT VS_Main(VS_INPUT input)
            {
                GS_INPUT output;
                output.pos = mul(unity_ObjectToWorld, float4(input.m_pos, 1.0));
                output.w_normal = mul(transpose((float3x3)unity_WorldToObject), input.normal);
                output.w_tangent = mul((float3x3)unity_ObjectToWorld, input.tangent.xyz);
                output.w_bitangent = cross(output.w_normal, output.w_tangent) * input.tangent.w;

                return output;
            }

            [maxvertexcount(18)] //GS单次调用可以输出的最大顶点数量,这里每个顶点会变成俩顶点所以是3*2;
            void GS_Main(triangle GS_INPUT gsIn[3], inout LineStream<FS_INPUT> triStream)
            {
                for(uint i = 0; i < 3; ++i){
                    FS_INPUT pIn;
                    pIn.pos = mul(unity_MatrixVP, gsIn[i].pos);

                    FS_INPUT pIn1;
                    float4 pos= gsIn[i].pos + float4(normalize(gsIn[i].w_normal), 0.0) * _LineLength;
                    pIn1.pos = mul(unity_MatrixVP, pos);
                    pIn.lineColor = _NormalLineColor;
                    pIn1.lineColor = _NormalLineColor;
                    triStream.Append(pIn);
                    triStream.Append(pIn1);
                    triStream.RestartStrip();

                    FS_INPUT pIn2;
                    pos = gsIn[i].pos + float4(normalize(gsIn[i].w_tangent), 0.0) *_LineLength;
                    pIn2.pos = mul(unity_MatrixVP, pos);
                    pIn.lineColor = _TangentLineColor;
                    pIn2.lineColor = _TangentLineColor;
                    triStream.Append(pIn);
                    triStream.Append(pIn2);
                    triStream.RestartStrip();

                    FS_INPUT pIn3;
                    pos = gsIn[i].pos + float4(normalize(gsIn[i].w_bitangent), 0.0) *_LineLength;
                    pIn3.pos = mul(unity_MatrixVP, pos);
                    pIn.lineColor = _BitangentLineColor;
                    pIn3.lineColor = _BitangentLineColor;
                    triStream.Append(pIn);
                    triStream.Append(pIn3);
                    triStream.RestartStrip();
                }
            }
 
            //step3
            float4 FS_Main(FS_INPUT input) : SV_TARGET0
            {
                return input.lineColor;
            }
        ENDHLSL
        }
    }
}

手动计算TNB

当然也可以手动计算切线空间的坐标系,但这样计算就会使得每个三角形面片都有自己独立的切线空间。例如,左图是模型默认切线空间,法线和切线由该顶点所参与的6个片元的法线和切线平均得到(平滑着色),右图是只根据法线在各片元内重新计算的切线和副切线(如果法线也各片元重新计算,就是平直着色)。【学习TNB矩阵过程中写的意义不明的Shader,而且法线用平均的但切线又重新算这是什么迷惑操作啊】

// Upgrade NOTE: replaced '_World2Object' with 'unity_WorldToObject'
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'
 
// Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld'
// Upgrade NOTE: replaced '_World2Object' with 'unity_WorldToObject'
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'
 
Shader "Custom/ShowNormals"
{
    Properties
    {
        _LineLength("LineLength", float) = 0.03
        _NormalLineColor("NormalLineColor", Color) = (1.0, 0.0, 0.0, 1.0)
        _TangentLineColor("TangentLineColor", Color) = (0.0, 1.0, 0.0, 1.0)
        _BitangentLineColor("BitangentLineColor", Color) = (0.0, 0.0, 1.0, 1.0)
    }
    SubShader
    {
        Pass
        {
 
            HLSLPROGRAM
                #pragma vertex VS_Main
                #pragma fragment FS_Main
                #pragma geometry GS_Main
 
                float4x4 unity_MatrixVP;
                float4x4 unity_ObjectToWorld;
                float4x4 unity_WorldToObject;

                float _LineLength;
                float4 _NormalLineColor;
                float4 _TangentLineColor;
                float4 _BitangentLineColor;

                 
                struct VS_INPUT{
                    float3 m_pos:POSITION;
                    float2 uv:TEXCOORD;
                    float3 normal:NORMAL;
                };
 
                struct GS_INPUT
                {
                    float4 pos:POSITION;
                    float2 uv:TEXCOORD0;
                    float3 normal:NORMAL;
                };
                struct FS_INPUT
                {
                    float4 pos:POSITION;
                    float4 lineColor:TEXCOORD0;
                };
                
                GS_INPUT VS_Main(VS_INPUT input)
                {
                    GS_INPUT output;
                    output.pos = mul(unity_ObjectToWorld, float4(input.m_pos, 1.0));
                    output.normal = normalize(mul(transpose((float3x3)unity_WorldToObject), input.normal));
                    output.uv = input.uv;

                    return output;
                }

                [maxvertexcount(18)] //GS单次调用可以输出的最大顶点数量,这里每个顶点会变成俩顶点所以是3*2;
                void GS_Main(triangle GS_INPUT gsIn[3], inout LineStream<FS_INPUT> triStream)
                {
                    float3 edge1 = gsIn[1].pos - gsIn[0].pos; 
                    float3 edge2 = gsIn[2].pos - gsIn[0].pos;
                    float2 deltaUV1 = gsIn[1].uv - gsIn[0].uv; 
                    float2 deltaUV2 = gsIn[2].uv - gsIn[0].uv; 

                    float f = 1.0 / (deltaUV1.x * deltaUV2.y - deltaUV2.x * deltaUV1.y);

                    float3 tangent;
                    tangent.x = f * (deltaUV2.y * edge1.x - deltaUV1.y * edge2.x);
                    tangent.y = f * (deltaUV2.y * edge1.y - deltaUV1.y * edge2.y);
                    tangent.z = f * (deltaUV2.y * edge1.z - deltaUV1.y * edge2.z);

                    float3 bitangent;
                    bitangent.x = f * (-deltaUV2.x * edge1.x + deltaUV1.x * edge2.x);
                    bitangent.y = f * (-deltaUV2.x * edge1.y + deltaUV1.x * edge2.y);
                    bitangent.z = f * (-deltaUV2.x * edge1.z + deltaUV1.x * edge2.z);

                    for(uint i = 0; i < 3; ++i){
                        float3 m_tangent = normalize(tangent - gsIn[i].normal * dot(gsIn[i].normal, tangent));
                        float3 m_bitangent = normalize(cross(tangent, gsIn[i].normal)); 

                        FS_INPUT pIn;
                        pIn.pos = mul(unity_MatrixVP, gsIn[i].pos);

                        FS_INPUT pIn1;
                        float4 pos= gsIn[i].pos + float4(gsIn[i].normal, 0) *_LineLength;
                        pIn1.pos = mul(unity_MatrixVP, pos);
                        pIn.lineColor = _NormalLineColor;
                        pIn1.lineColor = _NormalLineColor;
                        triStream.Append(pIn);
                        triStream.Append(pIn1);
                        triStream.RestartStrip();

                        FS_INPUT pIn2;
                        pos = gsIn[i].pos + float4(m_tangent, 0) *_LineLength;
                        pIn2.pos = mul(unity_MatrixVP, pos);
                        pIn.lineColor = _TangentLineColor;
                        pIn2.lineColor = _TangentLineColor;
                        triStream.Append(pIn);
                        triStream.Append(pIn2);
                        triStream.RestartStrip();

                        FS_INPUT pIn3;
                        pos = gsIn[i].pos + float4(m_bitangent, 0) *_LineLength;
                        pIn3.pos = mul(unity_MatrixVP, pos);
                        pIn.lineColor = _BitangentLineColor;
                        pIn3.lineColor = _BitangentLineColor;
                        triStream.Append(pIn);
                        triStream.Append(pIn3);
                        triStream.RestartStrip();
                    }
                }
 
                
                float4 FS_Main(FS_INPUT input) : SV_TARGET0
                {
                    return input.lineColor;
                }
            ENDHLSL
        }
    }
}

TNB矩阵和UV坐标关系

(不确定!)使用默认切线,在网格形变时可能会有问题?

把平面网格左下角的顶点移动到平面中心,世界空间下的法线出现了大问题(如下),左图的斜边应该是同样的颜色才对(即法线方向相同)。

梳理梳理TNB和法线贴图采样的过程导致问题的原因其实很明确,个人感觉对于会产生形变的网格,需要手动计算TBN矩阵时,考虑到TNB正交化对UV采样法线贴图的影响,需要对采样后的法线再增加一个和TNB正交化有关系的变换,可能后面会补充一下这部分的实现。

几何着色器:

https://zhuanlan.zhihu.com/p/585436751

DirectX11 With Windows SDK--15 几何着色器初探 - X_Jun - 博客园

渲染管线(主要是重新了解几何着色器):

一文读懂什么是渲染管线(7k字) - 天份& - 博客园

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2241565.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SDF,一个从1978年运行至今的公共Unix Shell

关于SDF 最近发现了一个很古老的公共Unix Shell服务器&#xff0c;这个项目从1978年运行至今&#xff0c;如果对操作系统&#xff0c;对Unix感兴趣&#xff0c;可以进去玩一玩体验一下 SDF Public Access UNIX System - Free Shell Account and Shell Access 注册方式 我一…

机器学习基础02_特征工程

目录 一、概念 二、API 三、DictVectorize字典列表特征提取 四、CountVectorize文本特征提取 五、TF-IDF文本1特征词的重要程度特征提取 六、无量纲化预处理 1、MinMaxScaler 归一化 2、StandardScaler 标准化 七、特征降维 1、特征选择 VarianceThreshold 底方差…

[前端面试]javascript

js数据类型 简单数据类型 null undefined string number boolean bigint 任意精度的大整数 symbol 创建唯一且不变的值&#xff0c;常用来表示对象属性的唯一标识 复杂数据类型 object&#xff0c;数组&#xff0c;函数,正则,日期等 区别 存储区别 简单数据类型因为其大小固定…

[DEBUG] 服务器 CORS 已经允许所有源,仍然有 304 的跨域问题

背景 今天有一台服务器到期了&#xff0c;准备把后端迁移到另一台服务器上&#xff0c;结果前端在测试的时候&#xff0c;出现了 304 的跨域问题。 调试过程中出现的问题&#xff0c;包括但不限于&#xff1a; set the request’s mode to ‘no-cors’Redirect is not allow…

深入理解接口测试:实用指南与最佳实践5.0(五)

✨博客主页&#xff1a; https://blog.csdn.net/m0_63815035?typeblog &#x1f497;《博客内容》&#xff1a;.NET、Java.测试开发、Python、Android、Go、Node、Android前端小程序等相关领域知识 &#x1f4e2;博客专栏&#xff1a; https://blog.csdn.net/m0_63815035/cat…

头歌网络安全(11.12)

头歌禁止复制解决 必须先下篡改猴&#xff01;&#xff01;&#xff01;&#xff01; 头歌复制助手 Educoder Copy Helperhttps://scriptcat.org/zh-CN/script-show-page/1860 Java生成验证码 第1关&#xff1a;使用Servlet生成验证码 任务描述 本关任务&#xff1a;使用se…

项目管理人员的自我评估与职业目标设定

在当今快速发展的商业环境中&#xff0c;项目管理人员的职业规划至关重要。它不仅涉及到个人职业发展的方向、目标和路径选择&#xff0c;还包括如何提升自身的专业技能、管理能力和行业知识。项目管理人员需要明确自己的职业目标、制定合理的职业发展计划、不断学习新知识和技…

关于 MSVCP110.dll 缺失的解决方案

背景&#xff1a;之前使用 PR&#xff08;Adobe Premiere&#xff09; 从来没有遇到过这样的问题。今天重装系统后&#xff08;window 10&#xff09;&#xff0c;想要重新安装以前的软件时&#xff0c;遇到了以下 DLL 文件缺失的错误。 解决方案&#xff1a; 可以到微软官网的…

036集——查询CAD图元属性字段信息:窗体显示(CAD—C#二次开发入门)

提取CAD图元所有属性字段&#xff0c;通过窗体显示&#xff0c;效果如下&#xff1a;&#xff08;curve改为entity&#xff09; 代码如下&#xff1a; public void 属性查询() {List<Curve> ents Z.db.SelectEntities<Curve>();if (ents is null ||ents.Cou…

反转链表

反转链表 给你单链表的头节点 head &#xff0c;请你反转链表&#xff0c;并返回反转后的链表。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5] 输出&#xff1a;[5,4,3,2,1]示例 2&#xff1a; 输入&#xff1a;head [1,2] 输出&#xff1a;[2,1]示例 3&#xff1…

使用 Ansys Mechanical 中的“螺栓工具”插件导出螺栓反作用力

概括&#xff1a; 对于处理复杂组件和结构的工程师和分析师来说&#xff0c;提高在 Ansys Mechanical 中提取多个螺栓反作用力表格的效率至关重要。在有限元分析 (FEA) 中&#xff0c;准确确定螺栓上的反作用力对于评估机械连接的完整性和性能至关重要。但是&#xff0c;手动提…

《FreeRTOS任务基础知识以及任务创建相关函数》

目录 1.FreeRTOS多任务系统与传统单片机单任务系统的区别 2.FreeRTOS中的任务&#xff08;Task&#xff09;介绍 2.1 任务特性 2.2 FreeRTOS中的任务状态 2.3 FreeRTOS中的任务优先级 2.4 在任务函数中退出 2.5 任务控制块和任务堆栈 2.5.1 任务控制块 2.5.2 任务堆栈…

【HAProxy09】企业级反向代理HAProxy高级功能之压缩功能与后端服务器健康性监测

HAProxy 高级功能 介绍 HAProxy 高级配置及实用案例 压缩功能 对响应给客户端的报文进行压缩&#xff0c;以节省网络带宽&#xff0c;但是会占用部分CPU性能 建议在后端服务器开启压缩功能&#xff0c;而非在HAProxy上开启压缩 注意&#xff1a;默认Ubuntu的包安装nginx开…

Gin 框架入门(GO)-1

解决安装包失败问题&#xff08;*&#xff09; go env -w GO111MODULEon go env -w GOPROXYhttps://goproxy.cn,direct 1 介绍 Gin 是一个 Go (Golang) 编写的轻量级 http web 框架&#xff0c;运行速度非常快&#xff0c;Gin 最擅长的就是 Api 接口的高并发。 2 Gin 环境搭建…

前端知识点---this的用法 , this动态绑定(Javascript)

文章目录 this动态绑定 , this的用法01. 全局作用域下的 this02. 函数中的 this2.1 普通函数调用2.2 构造函数调用2.3 箭头函数中的 this 03对象方法调用04. 事件处理中的 this05. 动态绑定的方式5.1 call 方法5.2 apply 方法5.3 bind 方法 06类中的 this07. 总结 this动态绑定…

Unity 跳过启动屏/Logo

使用官方API跳过Unity启动页 1.通过Unity的SplashScreen提供的接口 [Preserve]public class SkipSplash{[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSplashScreen)]private static void BeforeSplashScreen(){ #if UNITY_WEBGLApplication.focusChanged…

matplotlib2

第六部分&#xff1a;保存与导出图表 在实际的应用场景中&#xff0c;我们不仅需要在程序中展示图表&#xff0c;有时候还需要将这些图表保存为文件&#xff0c;以便在其他地方使用&#xff0c;比如插入文档、报告或网页中。matplotlib 提供了非常方便的保存图表功能。 6.1 保…

Linux卸载金仓KingBaseES数据库

Linux卸载金仓KingBaseES数据库 1、卸载前删除数据库服务2、图形化卸载3、控制台卸载4、静默卸载 1、卸载前删除数据库服务 如果在安装后执行root.sh脚本在系统中注册了数据库服务&#xff0c;需要在卸载前执行rootuninstall.sh脚本删除已注册的数据库服务。具体步骤如下&#…

【C#设计模式(11)——外观模式(Facade Pattern)】

前言 外观模式隐藏了子系统的复杂性&#xff0c;简化了客户端与子系统之间的交互。 代码 public class Facade{private CommunicationModel communicationModel;private AcquisitionModel acquisitionModel;private ToolModel toolModel;public Facade(){communicationModel n…

Spring Boot编程训练系统:数据管理与存储

摘要 随着信息技术在管理上越来越深入而广泛的应用&#xff0c;管理信息系统的实施在技术上已逐步成熟。本文介绍了编程训练系统的开发全过程。通过分析编程训练系统管理的不足&#xff0c;创建了一个计算机管理编程训练系统的方案。文章介绍了编程训练系统的系统分析部分&…