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进行测试需求分析——从电商网站需求到测试用例的生成
在软件测试工程师的日常工作中,需求分析是测试工作中的关键步骤。需求文档决定了测试覆盖的范围和测试策略,而测试用例的编写往往依赖于需求的准确理解。传统手工分析需求耗时长,尤其在面对大量需求和复杂逻辑时容易遗漏细节。本文将以电商网…...
深入探索:Scrapy深度爬取策略与实践
标题:深入探索:Scrapy深度爬取策略与实践 引言 在数据驱动的时代,深度爬取成为了获取丰富信息的重要手段。Scrapy,作为一个强大的Python爬虫框架,提供了多种工具和设置来帮助我们实现深度爬取。本文将详细介绍如何在…...
《生成式 AI》课程 第3講:訓練不了人工智慧嗎?你可以訓練你自己
资料来自李宏毅老师《生成式 AI》课程,如有侵权请通知下线 Introduction to Generative AI 2024 Spring 摘要 这一系列的作业是为 2024 年春季的《生成式 AI》课程设计的,共包含十个作业。每个作业都对应一个具体的主题,例如真假难辨的世界…...
如何编译 Cesium 源码
如何编译 Cesium 源码 Cesium 是一个开源的 JavaScript 库,用于构建 3D 地球和地图应用程序。它提供了一套强大的 API 和工具,使开发者能够创建丰富的地理空间应用。本文将指导您如何从 GitHub 下载 Cesium 源码,并在本地进行编译。 TilesB…...
前端开发设计模式——责任链模式
目录 一、定义和特点 1. 定义 2. 特点 二、实现方式 定义抽象处理者(Handler)类 创建具体处理者(ConcreteHandler)类 构建责任链 以下是一个用 JavaScript 实现的示例: 三、应用场景 1. 表单验证 2. 请求处…...
JavaWeb--MySQL
1. MySQL概述 首先来了解一下什么是数据库。 数据库:英文为 DataBase,简称DB,它是存储和管理数据的仓库。 像我们日常访问的电商网站京东,企业内部的管理系统OA、ERP、CRM这类的系统,以及大家每天都会刷的头条、抖音…...
Python | Leetcode Python题解之第564题数组嵌套
题目: 题解: class Solution:def arrayNesting(self, nums: List[int]) -> int:ans, n 0, len(nums)for i in range(n):cnt 0while nums[i] < n:num nums[i]nums[i] ni numcnt 1ans max(ans, cnt)return ans...
Spring Boot教程之Spring Boot简介
Spring Boot 简介 接下来一段时间,我会持续发布并完成Spring Boot教程 Spring 被广泛用于创建可扩展的应用程序。对于 Web 应用程序,Spring 提供了 Spring MVC,它是 Spring 的一个广泛使用的模块,用于创建可扩展的 Web 应用程序。…...
Qwen2-VL:发票数据提取、视频聊天和使用 PDF 的多模态 RAG 的实践指南
概述 随着人工智能技术的迅猛发展,多模态模型在各类应用场景中展现出强大的潜力和广泛的适用性。Qwen2-VL 作为最新一代的多模态大模型,融合了视觉与语言处理能力,旨在提升复杂任务的执行效率和准确性。本指南聚焦于 Qwen2-VL 在三个关键领域…...
【安全科普】NUMA防火墙诞生记
一、我为啥姓“NUMA” 随着网络流量和数据包处理需求的指数增长,曾经的我面对“高性能、高吞吐、低延迟”的要求,逐渐变得心有余而力不足。 多CPU技术应运而生,SMP(对称多处理)和NUMA(非一致性内存访问&a…...
机器学习day2-特征工程
四.特征工程 1.概念 一般使用pandas来进行数据清洗和数据处理、使用sklearn来进行特征工程 将任意数据(文本或图像等)转换为数字特征,对特征进行相关的处理 步骤:1.特征提取;2.无量纲化(预处理…...
沈阳做网站的设计公司哪家好/百度seo排名优化联系方式
创造实验数据 // 现在的时间 Date nowDate new Date(); // 设置三天后的时间 Calendar c Calendar.getInstance(); c.setTime(nowDate); c.add(Calendar.DATE,3); Date afterThreeDay c.getTime(); 默认向下取整天数 // 一天的毫秒数 long DAY 24L * 60L * 60L * 1000L; …...
外贸网站如何做推广/常用的seo查询工具
根据Lucas定理,等价于在$P$进制下每一位分别求组合数最后乘积模$P$。 因为答案为$0$的并不好算,所以可以考虑用$n1$减去其它所有的答案。 那么每一位的组合数都不能是$0$,那么这就保证了$k$的每一位都不大于$n$,所以无需考虑$k\le…...
网站简介 更改/八零云自助建站免费建站平台
if($category->user_id ! auth()->id()){throw new ModelNotFoundException();}...
赚钱网站入口/百度指数官网入口登录
污水中含磷量过高,是不允许排放的,过高的含磷量会导致水体富营养化,从而引发“绿潮”“水华”等水体污染,破坏水生态的自我净化能力,导致水底生物因为缺氧而死亡。所以不管是需要处理才能排放的污水,还是已…...
龙岗附近网站建设/百度网首页官网登录
BMP格式的图片怎在线压缩,大家在上传图片的时候有没有遇到过“因文件太大,上传失败”的问题,图片太大不仅占用空间,而且十分影响传递,那有没有什么解决方法呢,接下来让小编来告诉大家吧。第一、打开电脑&am…...
成都海鸥手表网站/公司在百度怎么推广
诊断Oracle Redo Log性能问题一、Rodo Log性能调整目标:在能够影响Oracle性能的诸多因素中,Redo Log相关的因素从某种程度上可以说是最为重要同时也是最值得关注的。因为在一个OLTP系统中Oracle通过各种技术以及优良的设计,尽量做到将大部分操…...