SRP 实现 Cook-Torrance BRDF
写的很乱!
BRDF(Bidirectional Reflectance Distribution Function)全称双向反射分布函数。辐射量单位非常多,这里为方便直观理解,会用非常不严谨的光照强度来解释说明。
BRDF光照模型,上反射率公式:
计算的是在法线所在半球上的所有入射光线,在出射方向的光强总和,公式有俩个部分。
即BRDF,从特定的入射方向到出射方向,在某个表面上,光强的衰减比率。
有了比率,还需要知道在物体表面接收到的某个方向上的光强,所以前半部分是光线沿入射方向到p点发射的光线强度,后半部分用表面与入射方向的夹角计算实际接收到的光线强度(因为这里虽然是一个点,但同时也需要当做一个极小的平面区域处理)。
关于几何函数
为什么GXX有俩个?只考虑一次反射能成功的,其实就是简单的认为入射方向能不被遮挡的光线比例 × 出射方向能不被遮挡的光线比例 = 整条光路上不会被遮挡的光线比例。
Cook-Torrance BRDF
🔻如果在渲染结果中发现了类似以下的问题或变,极大概率是法线没有在片元着色器中标准化。
SRP中实现的Cook-Torrance BRDF效果(无法线贴图,未进行gamma调整):
增加支持多光源
https://zhuanlan.zhihu.com/p/163360207
关于点光源获取位置和平行光获取方向。
获取位置用VisibleLight.localToWorldMatrix.GetColumn(3);
获取方向用VisibleLight.localToWorldMatrix.GetColumn(2);
因为localToWorldMatrix是光源的模型变换矩阵M,获取点光源的位置即mul(M, float4 (0, 0, 0, 1)),即localToWorldMatrix的最后一列。同理,平行光方向朝向局部坐标z轴,mul(M, float4 (0, 0, 1, 0))故为第三列。
有很多免费的材质素材: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 framevoid 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_Mainfloat4x4 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;};//step1GS_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();}}//step3float4 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_Mainfloat4x4 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字) - 天份& - 博客园
相关文章:

SRP 实现 Cook-Torrance BRDF
写的很乱! BRDF(Bidirectional Reflectance Distribution Function)全称双向反射分布函数。辐射量单位非常多,这里为方便直观理解,会用非常不严谨的光照强度来解释说明。 BRDF光照模型,上反射率公式&#…...
MySQL慢日志
慢查询日志顾名思义就是查询慢的sql语句可以记录到一个日志文件里,至于有多慢才会被记录,默认是10秒,但也可以通过系统配置来更改,慢日志在做系统优化时是一个非常好用的工具 #是否开启慢日志 show variables like slow_query_log…...
Flutter网络通信-封装Dio
前言 dio 是一个强大的 Dart HTTP 请求库,支持全局配置、Restful API、FormData、拦截器、 请求取消、Cookie 管理、文件上传/下载、超时以及自定义适配器等。 Dio的pub地址为:dio | Dart package 封装要求 能够使用get、post、put、patch、delete、…...
matlab 读取csv
需要跳过第一行表头等信息 1、读取整个文件 csvread(FILENAME)%文件路径 文件名2、指定起始位置 csvread(FILENAME, R, C)%从文件的第R行和第C列开始读取数据 逗号分开3、指定数据范围 csvread(FILENAME, R, C, [R1 C1 R2 C2])%读取从(R1, C1)到(R2, C2)范围内的数据注意&am…...

网络层9——虚拟专用网VPN和网络地址转换NAT
目录 一、为什么有虚拟专用网? 二、如何理解“虚拟专用网”? 三、IP隧道技术实现虚拟专用网 四、网络地址变换 一、为什么有虚拟专用网? 第一,IPv4只有32位,最多有40亿个全球唯一的IP地址数量不够,无法…...

开源科学工程技术软件介绍 – EDA工具KLayout
link 今天向各位知友介绍的 KLayout是一款由德国团队开发的开源EDA工具。 KLayout是使用C开发的,用户界面基于Qt。它支持Windows、MacOS和Linux操作系统。安装程序可以从下面的网址下载: https://www.klayout.de/build.html KLayout图形用户界面&…...
【网络安全】Cookie SameSite属性
未经许可,不得转载。 文章目录 背景CSRF 攻击SameSite 属性StrictLaxNone背景 为了有效防止 CSRF 攻击并保护用户隐私,Chrome 从 51 版本开始引入了 SameSite 属性,专门用于限制第三方 Cookie 的使用,进而减少安全风险。 CSRF 攻击 跨站请求伪造(CSRF)攻击是指恶意网站…...

Linux 命令 | 每日一学,文本处理三剑客之awk命令实践
[ 知识是人生的灯塔,只有不断学习,才能照亮前行的道路 ] 0x00 前言简述 描述:前面作者已经介绍了文本处理三剑客中的 grep 与 sed 文本处理工具,今天将介绍其最后一个且非常强大的 awk 文本处理输出工具,它可以非常方便…...

RabbitMQ的工作队列在Spring Boot中实现(详解常⽤的⼯作模式)
上文着重介绍RabbitMQ 七种工作模式介绍RabbitMQ 七种工作模式介绍_rabbitmq 工作模式-CSDN博客 本篇讲解如何在Spring环境下进⾏RabbitMQ的开发.(只演⽰部分常⽤的⼯作模式) 目录 引⼊依赖 一.工作队列模式 二.Publish/Subscribe(发布订阅模式) …...
【web前端笔记】vue3 + vite的前端项目中,使用import.meta.glob()方法实现全局注册组件的通用代码
目录 1.1、如何读取所有文件 1.2、通用代码 1.3、在main.js引入 这篇文章介绍一下,在vue3和vite搭建的项目中,如何将【src/components】目录下所有的【*.vue】文件,当做一个组件全局注册到Vue对象里面。 1.1、如何读取所有文件 在vue3和vite搭建的项目里面,它给我们提…...

保险行业建立知识管理系统:提高效率和安全性的策略
在保险行业,知识管理系统(KMS)的建立对于提高工作效率和保障数据安全性至关重要。保险公司需要在复杂的生态系统中航行,这个生态系统由不断发展的法规、错综复杂的保单和投保人不断变化的需求所定义。以下是一些关键策略ÿ…...

小程序如何完成订阅
小程序如何完成订阅 参考相关文档实践问题处理授权弹窗不再触发引导用户重新授权 参考相关文档 微信小程序实现订阅消息推送的实现步骤 发送订阅消息 小程序订阅消息(用户通过弹窗订阅)开发指南 实践 我们需要先选这一个模板,具体流程参考…...

JS学习日记(jQuery库)
前言 今天先更新jQuery库的介绍,它是一个用来帮助快速开发的工具 介绍 jQuery是一个快速,小型且功能丰富的JavaScript库,jQuery设计宗旨是“write less,do more”,即倡导写更少的代码,做更多的事…...

Uni-APP+Vue3+鸿蒙 开发菜鸟流程
参考文档 文档中心 运行和发行 | uni-app官网 AppGallery Connect DCloud开发者中心 环境要求 Vue3jdk 17 Java Downloads | Oracle 中国 【鸿蒙开发工具内置jdk17,本地不使用17会报jdk版本不一致问题】 开发工具 HBuilderDevEco Studio【目前只下载这一个就…...
Linux的基本用法
Linux的基本用法涵盖多个方面,包括用户登录、系统操作、文件和目录管理、系统工具使用等。以下是对Linux基本用法的详细介绍: 一、用户登录与系统操作 用户登录 普通用户登录:选择用户名并输入密码。超级用户(root)登…...
如何找出爬取网站的来源IP呢?
1.背景 最近网站数据库性能很不稳定,查询性能在某段时间很慢,服务器CPU也很高,平常时间很低,感觉被爬虫恶意搞了,因此我分析了一下最近的nginx访问日志 2.方法 找出访问量最大20个ip [root100 nginx]# cat liuhaih…...
Java爬虫(Jsoup)详解
文章目录 Java爬虫(Jsoup)详解一、引言二、Jsoup 快速入门1、Jsoup 简介1.1、添加依赖 2、解析 HTML 文档2.1、解析 HTML 字符串2.2、从 URL 加载 Document2.3、解析 body 片断 三、数据抽取1、使用 DOM 方法遍历文档3.1、获取元素 2、使用选择器语法查找…...

力扣周赛:第424场周赛
👨🎓作者简介:爱好技术和算法的研究生 🌌上期文章:力扣周赛:第422场周赛 📚订阅专栏:力扣周赛 希望文章对你们有所帮助 第一道题模拟题,第二道题经典拆分数组/线段树都…...

预处理(1)(手绘)
大家好,今天给大家分享一下编译器预处理阶段,那么我们来看看。 上面是一些预处理阶段的知识,那么明天给大家讲讲宏吧。 今天分享就到这里,谢谢大家!!...
利用OpenAI进行测试需求分析——从电商网站需求到测试用例的生成
在软件测试工程师的日常工作中,需求分析是测试工作中的关键步骤。需求文档决定了测试覆盖的范围和测试策略,而测试用例的编写往往依赖于需求的准确理解。传统手工分析需求耗时长,尤其在面对大量需求和复杂逻辑时容易遗漏细节。本文将以电商网…...
rknn优化教程(二)
文章目录 1. 前述2. 三方库的封装2.1 xrepo中的库2.2 xrepo之外的库2.2.1 opencv2.2.2 rknnrt2.2.3 spdlog 3. rknn_engine库 1. 前述 OK,开始写第二篇的内容了。这篇博客主要能写一下: 如何给一些三方库按照xmake方式进行封装,供调用如何按…...
SciencePlots——绘制论文中的图片
文章目录 安装一、风格二、1 资源 安装 # 安装最新版 pip install githttps://github.com/garrettj403/SciencePlots.git# 安装稳定版 pip install SciencePlots一、风格 简单好用的深度学习论文绘图专用工具包–Science Plot 二、 1 资源 论文绘图神器来了:一行…...

Swift 协议扩展精进之路:解决 CoreData 托管实体子类的类型不匹配问题(下)
概述 在 Swift 开发语言中,各位秃头小码农们可以充分利用语法本身所带来的便利去劈荆斩棘。我们还可以恣意利用泛型、协议关联类型和协议扩展来进一步简化和优化我们复杂的代码需求。 不过,在涉及到多个子类派生于基类进行多态模拟的场景下,…...
《Playwright:微软的自动化测试工具详解》
Playwright 简介:声明内容来自网络,将内容拼接整理出来的文档 Playwright 是微软开发的自动化测试工具,支持 Chrome、Firefox、Safari 等主流浏览器,提供多语言 API(Python、JavaScript、Java、.NET)。它的特点包括&a…...

【2025年】解决Burpsuite抓不到https包的问题
环境:windows11 burpsuite:2025.5 在抓取https网站时,burpsuite抓取不到https数据包,只显示: 解决该问题只需如下三个步骤: 1、浏览器中访问 http://burp 2、下载 CA certificate 证书 3、在设置--隐私与安全--…...

2025 后端自学UNIAPP【项目实战:旅游项目】6、我的收藏页面
代码框架视图 1、先添加一个获取收藏景点的列表请求 【在文件my_api.js文件中添加】 // 引入公共的请求封装 import http from ./my_http.js// 登录接口(适配服务端返回 Token) export const login async (code, avatar) > {const res await http…...
土地利用/土地覆盖遥感解译与基于CLUE模型未来变化情景预测;从基础到高级,涵盖ArcGIS数据处理、ENVI遥感解译与CLUE模型情景模拟等
🔍 土地利用/土地覆盖数据是生态、环境和气象等诸多领域模型的关键输入参数。通过遥感影像解译技术,可以精准获取历史或当前任何一个区域的土地利用/土地覆盖情况。这些数据不仅能够用于评估区域生态环境的变化趋势,还能有效评价重大生态工程…...
HTML前端开发:JavaScript 常用事件详解
作为前端开发的核心,JavaScript 事件是用户与网页交互的基础。以下是常见事件的详细说明和用法示例: 1. onclick - 点击事件 当元素被单击时触发(左键点击) button.onclick function() {alert("按钮被点击了!&…...

ArcGIS Pro制作水平横向图例+多级标注
今天介绍下载ArcGIS Pro中如何设置水平横向图例。 之前我们介绍了ArcGIS的横向图例制作:ArcGIS横向、多列图例、顺序重排、符号居中、批量更改图例符号等等(ArcGIS出图图例8大技巧),那这次我们看看ArcGIS Pro如何更加快捷的操作。…...
大学生职业发展与就业创业指导教学评价
这里是引用 作为软工2203/2204班的学生,我们非常感谢您在《大学生职业发展与就业创业指导》课程中的悉心教导。这门课程对我们即将面临实习和就业的工科学生来说至关重要,而您认真负责的教学态度,让课程的每一部分都充满了实用价值。 尤其让我…...