Games101,作业7(微表面模型)

2023-10-29

微表面模型

微表面模型属于材质方面,因此需要修改材质类Material.hpp

本片文章补充了https://blog.csdn.net/qq_36242312/article/details/116307626?spm=1001.2014.3001.5502博客中的一些细节。

BRFD

已知:

  • 入射方向 w i wi wi
  • 出射方向 w o wo wo
  • 法线 N N N

求:

  • 光线反射的(RGB)反射率 f r f_r fr

微表面模型的BRFD

基于物理的渲染(PBR)
f s = D ∗ G ∗ F 4 ∗ ( n ⃗ ⋅ l ⃗ ) ∗ ( n ⃗ ⋅ v ⃗ ) f_{s}=\frac{D * G * F}{4 *(\vec{n} \cdot \vec{l}) *(\vec{n} \cdot \vec{v})} fs=4(n l )(n v )DGF

  • D 法线分布函数:某个点上,法线指向指定方向上的概率。
  • G 几何函数:光线由于遮蔽、阴影,所以部分光线被阻挡;G即为光线没有被阻挡的概率。
  • F Fresnel方程:表示一束光线打在某个方向的平面上的反射的能量比率。
  • 4 ∗ ( n ⃗ ⋅ l ⃗ ) ∗ ( n ⃗ ⋅ v ⃗ ) {4 *(\vec{n} \cdot \vec{l}) *(\vec{n} \cdot \vec{v})} 4(n l )(n v )为矫正量

微表面模型需要用到的参数:

  • V V V:光线的反射方向(单位向量)
  • I I I:光线的入射方向的反方向(单位向量)
  • m = h m=h m=h V V V I I I的中间向量(单位向量)
  • N N N :为宏观平面法线(单位向量)
  • α \alpha α:为粗糙度 ∈ [ 0 , 1 ] \in[0,1] [0,1]

法线分布函数 D

微平面的法线分布函数D(m)描述了微观表面上的表面法线m的统计分布。


业界较为主流的法线分布函数是GGX(Trowbridge-Reitz),因为具有更好的高光长尾。
D ( n , h , α ) = α 2 π ( ( n ⋅ h ) 2 ( α 2 − 1 ) + 1 ) 2 D(n, h, \alpha) = \frac{\alpha^{2}}{\pi \left((n \cdot h)^{2}\left(\alpha^{2}-1\right)+1\right)^{2}} D(n,h,α)=π((nh)2(α21)+1)2α2

  • α \alpha α为粗糙度 ∈ [ 0 , 1 ] \in[0,1] [0,1]
  • n n n为宏观平面法线
  • h h h为微观平面法线,即 V V V I I I的中间向量。

α = 1 \alpha=1 α=1时, D = 1 π D = \frac 1 \pi D=π1,光线向半球面均匀发散;
α = 0 \alpha=0 α=0时,只有当 n = h 时 , D ≠ 0 n=h时,D \ne0 n=hD=0

如果想要理解函数是怎么来的,建议查阅相关论文,作为小白的我,已放弃挣扎~~

代码:
float D = DistributionGGX(N, H, roughness);

根据公式计算D值:

  • a α \alpha α
  • NdotH N 和 H N和H NH的点乘,必须大于0。
  • nom为分子
  • denom为分母(分母不能为0)。
 float DistributionGGX(Vector3f N, Vector3f H, float roughness)
    {
        float a = roughness * roughness;
        float a2 = a * a;
        float NdotH = std::max(dotProduct(N, H), 0.0f);
        float NdotH2 = NdotH * NdotH;

        float nom = a2;
        float denom = (NdotH2 * (a2 - 1.0) + 1.0);
        denom = M_PI * denom * denom;

        return nom / std::max(denom, 0.0000001f); // prevent divide by zero for roughness=0.0 and NdotH=1.0
    }

几何函数 G

目前较为常用的是其中最为简单的形式,分离遮蔽阴影(Separable Masking and Shadowing Function)。

该形式将几何项G分为两个独立的部分:光线方向(light)视线方向(view),并对两者用相同的分布函数来描述。

其中UE4的方案是Schlick-GGX,即基于Schlick近似,将k映射为 k = α 2 k = \frac {\alpha}{2} k=2α ,去匹配GGX Smith方程:

α = ( r o u g h t n e s s + 1 2 ) 2 − − − − − − − − − − − − k = α 2 − − − − − − − − − − − − − − − − G d i r e c t i o n ( n , v , k ) = n ⋅ v ( n ⋅ v ) ( 1 − k ) + k − − − − − − − − − − − − − − − − − − − − − G ( n , v , l , k ) = G v i e w ( n , v , k ) G l i g h t ( n , l , k ) \alpha = (\frac {roughtness+1}{2})^2 \\------------\\ k = \frac {\alpha}{2} \\----------------\\ G_{direction}(n, v, k)=\frac{n \cdot v}{(n \cdot v)(1-k)+k} \\---------------------\\ G(n, v, l, k)=G_{view}(n, v, k) G_{light}(n, l, k) α=(2roughtness+1)2k=2αGdirection(n,v,k)=(nv)(1k)+knvG(n,v,l,k)=Gview(n,v,k)Glight(n,l,k)
G ( n , v , l , k ) G(n, v, l, k) G(n,v,l,k)返回遮蔽阴影函数的计算结果。

  • 粗糙度 α = 0 \alpha = 0 α=0时,完全光滑,光线通过率为1.
  • 粗糙度 α = 1 \alpha = 1 α=1时,最粗糙,光线通过率为 n ⋅ v n \cdot v nv

因此 G ( n , v , l , k ) ∈ [ ( n ⋅ v ) ( n ⋅ l ) , 1 ] G(n, v, l, k) \in [(n \cdot v)(n \cdot l),1] G(n,v,l,k)[(nv)(nl),1]

代码:
float G = GeometrySmith(N, V, L, roughness);
  • N为宏观表面法线
  • V为光线反射方向(可理解为视口的方向)
  • L为光线进入入的反方向(可理解为光源的方向)
  • roughness为粗糙度。
    float GeometrySchlickGGX(float NdotV, float k)
    {
        float nom = NdotV;
        float denom = NdotV * (1.0 - k) + k;

        return nom / denom;
    }

    float GeometrySmith(Vector3f N, Vector3f V, Vector3f L, float roughness)
    {
        float r = (roughness + 1.0);
        float k = (r * r) / 8.0;
        float NdotV = std::max(dotProduct(N, V), 0.0f);
        float NdotL = std::max(dotProduct(N, L), 0.0f);
        float ggx2 = GeometrySchlickGGX(NdotV, k);
        float ggx1 = GeometrySchlickGGX(NdotL, k);

        return ggx1 * ggx2;
    }

Fresnel方程 F

对于菲涅尔(Fresnel)项,业界方案一般都采用Schlick的Fresnel近似,因为计算成本低廉,而且精度足够:
F s c h l i c k ( v , h ) = F 0 + ( 1 − F 0 ) ( 1 − ( v ⋅ h ) ) 5 F_{schlick}(v,h) = F_0 + (1-F_0)(1-(v \cdot h))^5 Fschlick(v,h)=F0+(1F0)(1(vh))5

但是在项目中没有使用近似计算,而是使用了Fresnel方程。

  • R S R_S RS为S偏振光
  • R P R_P RP为P偏振光

分别对应的菲涅尔方程为:
在这里插入图片描述
在这里插入图片描述
详细内容看菲涅尔方程(Fresnel Equation)

代码
  • i为入射角度
  • t为折射角度
  • ior为平面物体的折射率(法线反向空间中介质的折射率)
  • kr为需要计算的反射率(即反射的光线所占的比率)。

在这里插入图片描述

// 计算 fresnel 系数: F
float F;
float etat = 1.85;
fresnel(wi, N, etat, F);
void fresnel(const Vector3f &I, const Vector3f &N, const float &ior, float &kr) const
    {
        float cosi = clamp(-1, 1, dotProduct(I, N));
        float etai = 1, etat = ior;
        if (cosi > 0) {  std::swap(etai, etat); }
        // Compute sini using Snell's law
        float sint = etai / etat * sqrtf(std::max(0.f, 1 - cosi * cosi));
        // Total internal reflection
        if (sint >= 1) {
            kr = 1;
        }
        else {
            float cost = sqrtf(std::max(0.f, 1 - sint * sint));
            cosi = fabsf(cosi);
            float Rs = ((etat * cosi) - (etai * cost)) / ((etat * cosi) + (etai * cost));
            float Rp = ((etai * cosi) - (etat * cost)) / ((etai * cosi) + (etat * cost));
            kr = (Rs * Rs + Rp * Rp) / 2;
        }
        // As a consequence of the conservation of energy, transmittance is given by:
        // kt = 1 - kr;
    }

微表面模型的BRDF

Cook-Torrance BRDF兼有漫反射和镜面反射两个部分:

f r = k d ∗ f d + k s ∗ f s f_r=k_d*f_{d}+k_s*f_{s} fr=kdfd+ksfs

微表面镜面反射项

f s = D ∗ G ∗ F 4 ∗ ( n ⃗ ⋅ l ⃗ ) ∗ ( n ⃗ ⋅ v ⃗ ) f_{s}=\frac{D * G * F}{4 *(\vec{n} \cdot \vec{l}) *(\vec{n} \cdot \vec{v})} fs=4(n l )(n v )DGF

Vector3f nominator = D * G * F;
float denominator = 4 * std::max(dotProduct(N, V), 0.0f) \
 					  * std::max(dotProduct(N, L), 0.0f);
Vector3f specular = nominator / std::max(denominator, 0.001f);

specular 即为 f s f_s fs

微表面漫反射项

f d = c π f_d = \frac c \pi fd=πc

  • c表示表面颜色
  • 除以π是为了对漫反射光进行标准化,因为前面含有BRDF的积分方程是受π影响的。

此处我也没搞懂,以后再填坑

其他项
  • Kd:为折射入物体内的光线从内部弹射出来后,没有被损失的能量以漫反射的方式分散到各个方向。
  • Ks:为反射的颜色分量,与物体表面颜色有关。
代码
// 能量守恒
float ks_ = F;//反射比率
float kd_ = 1.0f - ks_;//折射比率

Vector3f diffuse = 1.0f / M_PI;//漫反射系数

// 因为在 specular 项里已经考虑了反射部分的比例:F。所以反射部分不需要再乘以 ks_ 
return Ks * specular + kd_ * Kd * diffuse;

完整代码

private:
    float DistributionGGX(Vector3f N, Vector3f H, float roughness)
    {
        float a = roughness * roughness;
        float a2 = a * a;
        float NdotH = std::max(dotProduct(N, H), 0.0f);
        float NdotH2 = NdotH * NdotH;

        float nom = a2;
        float denom = (NdotH2 * (a2 - 1.0) + 1.0);
        denom = M_PI * denom * denom;

        return nom / std::max(denom, 0.0000001f); // prevent divide by zero for roughness=0.0 and NdotH=1.0
    }

    float GeometrySchlickGGX(float NdotV, float k)
    {
        float nom = NdotV;
        float denom = NdotV * (1.0 - k) + k;
        return nom / denom;
    }

    float GeometrySmith(Vector3f N, Vector3f V, Vector3f L, float roughness)
    {
        float r = (roughness + 1.0);
        float k = (r * r) / 8.0;
        float NdotV = std::max(dotProduct(N, V), 0.0f);
        float NdotL = std::max(dotProduct(N, L), 0.0f);
        float ggx2 = GeometrySchlickGGX(NdotV, k);
        float ggx1 = GeometrySchlickGGX(NdotL, k);

        return ggx1 * ggx2;
    }

Vector3f Material::eval(const Vector3f &wi, const Vector3f &wo, const Vector3f &N){
    switch(m_type){
        case DIFFUSE:
        {
            // calculate the contribution of diffuse   model
            float cosalpha = dotProduct(N, wo);
            if (cosalpha > 0.0f) {
                Vector3f diffuse = Kd / M_PI;
                return diffuse;
            }
            else
                return Vector3f(0.0f);
            break;
        }
        case Microfacet://微表面材质的BRDF
        {
            // Disney PBR 方案
            float cosalpha = dotProduct(N, wo);
            if (cosalpha > 0.0f) {
                float roughness = 0.40;

                Vector3f V = -wi;
                Vector3f L = wo;
                Vector3f H = normalize(V + L);

                // 计算 distribution of normals: D
                float D = DistributionGGX(N, H, roughness);

                // 计算 shadowing masking term: G
                float G = GeometrySmith(N, V, L, roughness);

                // 计算 fresnel 系数: F
                float F;
                float etat = 1.85;
                fresnel(wi, N, etat, F);

                Vector3f nominator = D * G * F;
                float denominator = 4 * std::max(dotProduct(N, V), 0.0f) * std::max(dotProduct(N, L), 0.0f);
                Vector3f specular = nominator / std::max(denominator, 0.001f);

                // 能量守恒
                float ks_ = F;//反射比率
                float kd_ = 1.0f - ks_;//折射比率

                Vector3f diffuse = 1.0f / M_PI;

                // 因为在 specular 项里已经考虑了反射部分的比例:F。所以反射部分不需要再乘以 ks_ 
                //Ks为镜面反射项,Kd为漫反射项。
                return Ks * specular + kd_ * Kd * diffuse;
            }
            else
                return Vector3f(0.0f);
            break;
        }
    }
}

其他需要修改的函数

in Material.hpp

Vector3f Material::sample(const Vector3f &wi, const Vector3f &N){
    switch(m_type){
    case DIFFUSE:
    case Microfacet:
        {
            // uniform sample on the hemisphere在半球上均匀采样
            float x_1 = get_random_float(), x_2 = get_random_float();
            //z∈[0,1],是随机半球方向的z轴向量
            float z = std::fabs(1.0f - 2.0f * x_1);
            //r是半球半径随机向量以法线为旋转轴的半径
            //phi是r沿法线旋转轴的旋转角度
            float r = std::sqrt(1.0f - z * z), phi = 2 * M_PI * x_2;//phi∈[0,2*pi]
            Vector3f localRay(r*std::cos(phi), r*std::sin(phi), z);//半球面上随机的光线的弹射方向
            return toWorld(localRay, N);//转换到世界坐标
            
            break;
        }
    }
}

float Material::pdf(const Vector3f &wi, const Vector3f &wo, const Vector3f &N){
    switch(m_type){
        case DIFFUSE:
        case Microfacet:
        {
            // uniform sample probability 1 / (2 * PI)
            if (dotProduct(wo, N) > 0.0f)
                return 0.5f / M_PI;
            else
                return 0.0f;
            break;
        }
    }
}

in sphere.hpp

1.计算误差导致的交点偏离问题

Intersection getIntersection(Ray ray)函数中修改圆的相交判定,因为圆的相交判定精度不够。
若不修改程序在运行中会报错,因为当 t 0 = 0 t0=0 t0=0 时,有可能会造成除0错误

	Intersection getIntersection(Ray ray){
        Intersection result;
        result.happened = false;
        Vector3f L = ray.origin - center;
        float a = dotProduct(ray.direction, ray.direction);
        float b = 2 * dotProduct(ray.direction, L);
        float c = dotProduct(L, L) - radius2;
        float t0, t1;
        if (!solveQuadratic(a, b, c, t0, t1)) return result;
        if (t0 < 0) t0 = t1;
        if (t0 < 0) return result;
        

        //精度不够
        if (t0 > 0.5) {
            result.happened = true;

            result.coords = Vector3f(ray.origin + ray.direction * t0);
            result.normal = normalize(Vector3f(result.coords - center));
            result.m = this->m;
            result.obj = this;
            result.distance = t0;
        }
        return result;
    }

生成的图片为:
在这里插入图片描述

若设置为 if (t0 != 0.0) ,则代码不会在运行中报错,但是会生成有黑点的图像。
blackSphere
若设置精度为t>0.1,生成的图像为:
在这里插入图片描述

可以看出t>0.1t>0.5的差别并不大。那为什么(t!=0t>0)这样的条件会导致图像黑点呢???

下面我们来分析原因:

光线与球体求交

光线与球体求交公式使用了一般情况下的求交公式(在该项目中可以优化求交公式。因为ray.origin即光源位置;ray.direction为单位向量)。

而求交公式获得的交点有偏差,如下图:请添加图片描述
实际光线与球面相交返回的交点在球面附近
根据程序流程,找到某个物体的交点后,要将该交点和某个光源连线,判断该点光源是否可见。

  • 若交点在球内部,则若按照一般情况判断,交点和光源的连线必然会被该球体遮蔽,光路可能在球体内部弹射(这不符合实际)。
  • 若交点在球外部,则该点可以被光源或其他弹射光线照亮。

因此会出现上图中的有黑点的球体。

算法纠正:

交点和光源的连线球面相交时,若球面距离弹射点过近,则认为球面和光线不相交。这样便可避免上方的错误。

这便是在Intersection getIntersection(Ray ray)函数中要限定t>某个值的意义!!!

2.无返回值

在下面函数中添加返回值,不然会报错。

Vector3f evalDiffuseColor(const Vector2f &st)const {
        //return m->getColor();
        return {};
}

最终渲染效果

请添加图片描述

请添加图片描述

最后祝大家新春快乐
请添加图片描述

参考资料:

GAMES101-现代计算机图形学学习笔记(作业07)
从零开始学图形学:写一个光线追踪渲染器(二)——微表面模型与代码实现
LearnOpenGL_CN 理论
【基于物理的渲染(PBR)白皮书】(一) 开篇:PBR核心知识体系总结与概览
【基于物理的渲染(PBR)白皮书】(四)法线分布函数相关总结
【基于物理的渲染(PBR)白皮书】(五)几何函数相关总结
计算机图形学——学习记录三(光线相交)
菲涅尔方程(Fresnel Equation)

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Games101,作业7(微表面模型) 的相关文章

  • 删除文件的最后 10 个字符

    我想删除文件的最后 10 个字符 说一个字符串 hello i am a c learner 是文件内的数据 我只是希望该文件是 hello i am a 文件的最后 10 个字符 即字符串 c learner 应在文件内消除 解决方案 将
  • 如何将 std::string& 转换为 C# 引用字符串

    我正在尝试将 C 函数转换为std string参考C 我的 API 如下所示 void GetStringDemo std string str 理想情况下 我希望在 C 中看到类似的东西 void GetStringDemoWrap r
  • 在 xaml 中编写嵌套类型时出现设计时错误

    我创建了一个用户控件 它接受枚举类型并将该枚举的值分配给该用户控件中的 ComboBox 控件 很简单 我在数据模板中使用此用户控件 当出现嵌套类型时 问题就来了 我使用这个符号来指定 EnumType x Type myNamespace
  • 如何在 Cassandra 中存储无符号整数?

    我通过 Datastax 驱动程序在 Cassandra 中存储一些数据 并且需要存储无符号 16 位和 32 位整数 对于无符号 16 位整数 我可以轻松地将它们存储为有符号 32 位整数 并根据需要进行转换 然而 对于无符号 64 位整
  • C++11 删除重写方法

    Preface 这是一个关于最佳实践的问题 涉及 C 11 中引入的删除运算符的新含义 当应用于覆盖继承父类的虚拟方法的子类时 背景 根据标准 引用的第一个用例是明确禁止调用某些类型的函数 否则转换将是隐式的 例如最新版本第 8 4 3 节
  • 如何从 Visual Studio 将视图导航到其控制器?

    问题是解决方案资源管理器上有 29 个项目 而且项目同时具有 ASP NET MVC 和 ASP NET Web 表单结构 在MVC部分中 Controller文件夹中有大约100个子文件夹 每个文件夹至少有3 4个控制器 视图完全位于不同
  • std::vector 与 std::stack

    有什么区别std vector and std stack 显然 向量可以删除集合中的项目 尽管比列表慢得多 而堆栈被构建为仅后进先出的集合 然而 堆栈对于最终物品操作是否更快 它是链表还是动态重新分配的数组 我找不到关于堆栈的太多信息 但
  • WPF 数据绑定到复合类模式?

    我是第一次尝试 WPF 并且正在努力解决如何将控件绑定到使用其他对象的组合构建的类 例如 如果我有一个由两个单独的类组成的类 Comp 为了清楚起见 请注意省略的各种元素 class One int first int second cla
  • 人脸 API DetectAsync 错误

    我想创建一个简单的程序来使用 Microsoft Azure Face API 和 Visual Studio 2015 检测人脸 遵循 https social technet microsoft com wiki contents ar
  • C# 列表通用扩展方法与非通用扩展方法

    这是一个简单的问题 我希望 集合类中有通用和非通用方法 例如List
  • 两个静态变量同名(两个不同的文件),并在任何其他文件中 extern 其中一个

    在一个文件中将变量声明为 static 并在另一个文件中进行 extern 声明 我认为这会在链接时出现错误 因为 extern 变量不会在任何对象中看到 因为在其他文件中声明的变量带有限定符 static 但不知何故 链接器 瑞萨 没有显
  • 为什么这个字符串用AesCryptoServiceProvider第二次解密时不相等?

    我在 C VS2012 NET 4 5 中的文本加密和解密方面遇到问题 具体来说 当我加密并随后解密字符串时 输出与输入不同 然而 奇怪的是 如果我复制加密的输出并将其硬编码为字符串文字 解密就会起作用 以下代码示例说明了该问题 我究竟做错
  • x:将 ViewModel 方法绑定到 DataTemplate 内的事件

    我基本上问同样的问题这个人 https stackoverflow com questions 10752448 binding to viewmodels property from a template 但在较新的背景下x Bind V
  • 两个类可以使用 C++ 互相查看吗?

    所以我有一个 A 类 我想在其中调用一些 B 类函数 所以我包括 b h 但是 在 B 类中 我想调用 A 类函数 如果我包含 a h 它最终会陷入无限循环 对吗 我能做什么呢 仅将成员函数声明放在头文件 h 中 并将成员函数定义放在实现文
  • 实例化类时重写虚拟方法

    我有一个带有一些虚函数的类 让我们假设这是其中之一 public class AClassWhatever protected virtual string DoAThingToAString string inputString retu
  • C 编程:带有数组的函数

    我正在尝试编写一个函数 该函数查找行为 4 列为 4 的二维数组中的最大值 其中二维数组填充有用户输入 我知道我的主要错误是函数中的数组 但我不确定它是什么 如果有人能够找到我出错的地方而不是编写新代码 我将不胜感激 除非我刚去南方 我的尝
  • 空指针与 int 等价

    Bjarne 在 C 编程语言 中写道 空指针与整数零不同 但 0 可以用作空指针的指针初始值设定项 这是否意味着 void voidPointer 0 int zero 0 int castPointer reinterpret cast
  • 在 WPF 中使用 ReactiveUI 提供长时间运行命令反馈的正确方法

    我有一个 C WPF NET 4 5 应用程序 用户将用它来打开某些文件 然后 应用程序将经历很多动作 读取文件 通过许多插件和解析器传递它 这些文件可能相当大 gt 100MB 因此这可能需要一段时间 我想让用户了解 UI 中发生的情况
  • C# 中的 IPC 机制 - 用法和最佳实践

    不久前我在 Win32 代码中使用了 IPC 临界区 事件和信号量 NET环境下场景如何 是否有任何教程解释所有可用选项以及何时使用以及为什么 微软最近在IPC方面的东西是Windows 通信基础 http en wikipedia org
  • 为什么 std::uint32_t 与 uint32_t 不同?

    我对 C 有点陌生 我有一个编码作业 很多文件已经完成 但我注意到 VS2012 似乎有以下语句的问题 typedef std uint32 t identifier 不过 似乎将其更改为 typedef uint32 t identifi

随机推荐

  • (Struts2学习篇) Struts2数据校验之二

    struts2数据校验的方法 1 validateXxx方法验证 validate多业务action方法的验证 如果业务验证方法不止一个则可以使用validateXXX 来验证 在struts xml配置方法调用 span style fo
  • OSI与TCP/IP的区别

    OSI的概念 Open System Interconnect开放系统互连参考模型 是由ISO 国际标准化组织 定义的 它是个灵活的 稳健的和可互操作的模型 并不是协 议 常用来分析和设计网络体系结构 OSI模型分为七层 OSI把网络按照层
  • 身份识别与访问管理(IAM)

    身份识别与访问管理 IAM Identity and Access Management 参考文章 涨知识 认识IAM 向着 零信任 安全架构迈进 什么是身份和访问管理 IAM IAM 身份识别与访问管理 简称大4A 百度百科 身份访问与管
  • EDA第一次课<1117电路图的绘制>

    EDA第一次课 lt 1117电路图的绘制 gt 电路图1117 我们把需要用到的元器件先列出来 PWR2 5 1 GND 3 RES2 4 LED0 2 VCC 1 双刀双掷开关 1 自制元器件 黄框框 1 绘制1117 1 首先我们打开
  • 【自然语言处理】关系抽取 —— CoIn 讲解

    CoIn 论文信息 标题 Consistent Inference for Dialogue Relation Extraction 作者 Xinwei Long Shuzi Niu Yucheng Li 期刊 IJCAI 2021 主题
  • Golang 结构化日志包 log/slog 详解(三):属性字段和日志级别

    上一篇文章讲解了 log slog 包中的 Handler 的使用方法 通过不同的 Handler 可以输出不同格式的日志 接下来看一下如何自定义日志的属性字段和日志级别 属性字段 attribute 许多日志都有一些通用的的字段 例如日志
  • [每日一氵]Latex 的通讯作者怎么搞

    之前看文章 通讯作者都是默认放在最后一个位置 然后用十字或者双十字标注上 今天看到这个模板的通讯作者 还是这个好看 机构 标题都是我瞎编的 title Rethinking the eyes Problem in Game author D
  • 安装完华为的opengauss 后,能ping通服务地址,telnet不通端口

    一 事件起因 安装完华为的openGauss 后 能ping通服务的地址 telnet不通端口 linux服务本机可以用gsql客户端访问 但是外部用 Data Studio 或者 Navicat 进行连接时 连接服务失败 利用 cmd 进
  • 大牛深入讲解!最经典的HashMap图文详解

    栈和队列部分 10 设计一个有getMin功能的栈 士 由两个栈组成的队列 尉 如何仅用递归函数和栈操作逆序一个栈 尉 猫狗队列 士 用一个栈实现另一个栈的排序 士 用栈来求解汉诺塔问题 校 生成窗口最大值数组 尉 构造数组的MaxTree
  • AStyle.exe -h的完整内容,方便配置时查阅

    AStyle exe h Artistic Style 3 1 Maintained by Jim Pattee Original Author Tal Davidson Usage astyle OPTIONS File1 File2 F
  • 如何剪辑一个好的短视频?教你一个小技巧,剪辑视频很简单

    短视频现在越来越受到大众喜欢 不管是老人 小孩 年轻人都会在有空余时间刷下短视频 不知不觉得短视频已形成人们平时生活中一部分 那么要如何剪辑一个好的短视频 制作短视频可以分成两个部分 第一部分就是选题再拍一个短视频这个一般都很容易操作的 第
  • lua 函数 默认值_Unity热更新框架之xLua

    一 xLua概述 二 Lua文件加载 三 xLua文件配置 四 Lua与C 交互 五 xLua热更新 一 xLua概述 1 1 xLua简介 xLua是由腾讯维护的一个开源项目 xLua为Unity Net Mono等C 环境增加Lua脚本
  • 计算某字符出现次数

    写出一个程序 接受一个由字母 数字和空格组成的字符串 和一个字符 然后输出输入字符串中该字符的出现次数 不区分大小写字母 数据范围 1 n 1000 输入描述 第一行输入一个由字母和数字以及空格组成的字符串 第二行输入一个字符 输出描述 输
  • Python深度学习实用代码合集

    Python深度学习实用代码合集 Colab 代码集合 怎么在colab打开ipynb文件 保存文件夹内的所有文件 从github拉代码 Python代码集合 可交互选取文件路径 文件目录 遍历文件夹批量修改文件名和后缀 复制文件 删除文件
  • 数据库中distinct的用法,distinct和聚合函数一起使用,distinct的位置

    distinct的详细用法 distinct的基本用法 distinct和聚合函数 distinct的位置 distinct的基本用法 一 house表 表结构如图 字段id house name floor address 二 上图除了i
  • 命名实体识别方法汇总

    最近在学习命名实体识别 在查阅资料的同时 对命名实体识别的概念以及常用方法做了一下整理汇总 方便以后学习和查阅 命名实体识别 Named EntitiesRecognition NER 是自然语言处理 Natural LanguagePro
  • Linux-VI和VIM

    目录 VI的使用 VI的三种模式 进入VI 切换至插入模式 Insert mode 编辑文件 Insert 的切换 退出VI及保存文件 搜索 快捷删除 光标定位到最后一行 VIM查找字符串 全匹配 模糊匹配 正则表达式 快速查找 VI的使用
  • OpenWRT新建普通用户+权限设置+免密码登录(ssh公钥验证)

    目的 提升系统安全 区分客户及管理员 偶然在做https加密的时候看到有关于如何openwrt系统安全加固的教程 以https方式进入openwrt管理页面属于其中一种 Secure your router s access http wi
  • JavaScript 垃圾回收机制

    0 何为垃圾回收 看下面例子 let test name codereasy test 1 2 3 4 5 在JavaScript中 引用类型保存在堆内存中 然后在栈内存中保存一个对堆内存中实际对象的引用 在上述例子中 我们声明了一个变量
  • Games101,作业7(微表面模型)

    微表面模型 微表面模型属于材质方面 因此需要修改材质类Material hpp 本片文章补充了https blog csdn net qq 36242312 article details 116307626 spm 1001 2014 3