当前位置: 首页 > news >正文

SRP 实现 Cook-Torrance BRDF

写的很乱!

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调整):

增加支持多光源

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

写的很乱&#xff01; BRDF&#xff08;Bidirectional Reflectance Distribution Function&#xff09;全称双向反射分布函数。辐射量单位非常多&#xff0c;这里为方便直观理解&#xff0c;会用非常不严谨的光照强度来解释说明。 BRDF光照模型&#xff0c;上反射率公式&#…...

MySQL慢日志

慢查询日志顾名思义就是查询慢的sql语句可以记录到一个日志文件里&#xff0c;至于有多慢才会被记录&#xff0c;默认是10秒&#xff0c;但也可以通过系统配置来更改&#xff0c;慢日志在做系统优化时是一个非常好用的工具 #是否开启慢日志 show variables like slow_query_log…...

Flutter网络通信-封装Dio

前言 dio 是一个强大的 Dart HTTP 请求库&#xff0c;支持全局配置、Restful API、FormData、拦截器、 请求取消、Cookie 管理、文件上传/下载、超时以及自定义适配器等。 Dio的pub地址为&#xff1a;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

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

开源科学工程技术软件介绍 – EDA工具KLayout

link 今天向各位知友介绍的 KLayout是一款由德国团队开发的开源EDA工具。 KLayout是使用C开发的&#xff0c;用户界面基于Qt。它支持Windows、MacOS和Linux操作系统。安装程序可以从下面的网址下载&#xff1a; https://www.klayout.de/build.html KLayout图形用户界面&…...

【网络安全】Cookie SameSite属性

未经许可,不得转载。 文章目录 背景CSRF 攻击SameSite 属性StrictLaxNone背景 为了有效防止 CSRF 攻击并保护用户隐私,Chrome 从 51 版本开始引入了 SameSite 属性,专门用于限制第三方 Cookie 的使用,进而减少安全风险。 CSRF 攻击 跨站请求伪造(CSRF)攻击是指恶意网站…...

Linux 命令 | 每日一学,文本处理三剑客之awk命令实践

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

RabbitMQ的工作队列在Spring Boot中实现(详解常⽤的⼯作模式)

上文着重介绍RabbitMQ 七种工作模式介绍RabbitMQ 七种工作模式介绍_rabbitmq 工作模式-CSDN博客 本篇讲解如何在Spring环境下进⾏RabbitMQ的开发.&#xff08;只演⽰部分常⽤的⼯作模式&#xff09; 目录 引⼊依赖 一.工作队列模式 二.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搭建的项目里面,它给我们提…...

保险行业建立知识管理系统:提高效率和安全性的策略

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

小程序如何完成订阅

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

JS学习日记(jQuery库)

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

Uni-APP+Vue3+鸿蒙 开发菜鸟流程

参考文档 文档中心 运行和发行 | uni-app官网 AppGallery Connect DCloud开发者中心 环境要求 Vue3jdk 17 Java Downloads | Oracle 中国 【鸿蒙开发工具内置jdk17&#xff0c;本地不使用17会报jdk版本不一致问题】 开发工具 HBuilderDevEco Studio【目前只下载这一个就…...

Linux的基本用法

Linux的基本用法涵盖多个方面&#xff0c;包括用户登录、系统操作、文件和目录管理、系统工具使用等。以下是对Linux基本用法的详细介绍&#xff1a; 一、用户登录与系统操作 用户登录 普通用户登录&#xff1a;选择用户名并输入密码。超级用户&#xff08;root&#xff09;登…...

如何找出爬取网站的来源IP呢?

1.背景 最近网站数据库性能很不稳定&#xff0c;查询性能在某段时间很慢&#xff0c;服务器CPU也很高&#xff0c;平常时间很低&#xff0c;感觉被爬虫恶意搞了&#xff0c;因此我分析了一下最近的nginx访问日志 2.方法 找出访问量最大20个ip [root100 nginx]# cat liuhaih…...

Java爬虫(Jsoup)详解

文章目录 Java爬虫&#xff08;Jsoup&#xff09;详解一、引言二、Jsoup 快速入门1、Jsoup 简介1.1、添加依赖 2、解析 HTML 文档2.1、解析 HTML 字符串2.2、从 URL 加载 Document2.3、解析 body 片断 三、数据抽取1、使用 DOM 方法遍历文档3.1、获取元素 2、使用选择器语法查找…...

力扣周赛:第424场周赛

&#x1f468;‍&#x1f393;作者简介&#xff1a;爱好技术和算法的研究生 &#x1f30c;上期文章&#xff1a;力扣周赛&#xff1a;第422场周赛 &#x1f4da;订阅专栏&#xff1a;力扣周赛 希望文章对你们有所帮助 第一道题模拟题&#xff0c;第二道题经典拆分数组/线段树都…...

预处理(1)(手绘)

大家好&#xff0c;今天给大家分享一下编译器预处理阶段&#xff0c;那么我们来看看。 上面是一些预处理阶段的知识&#xff0c;那么明天给大家讲讲宏吧。 今天分享就到这里&#xff0c;谢谢大家&#xff01;&#xff01;...

利用OpenAI进行测试需求分析——从电商网站需求到测试用例的生成

在软件测试工程师的日常工作中&#xff0c;需求分析是测试工作中的关键步骤。需求文档决定了测试覆盖的范围和测试策略&#xff0c;而测试用例的编写往往依赖于需求的准确理解。传统手工分析需求耗时长&#xff0c;尤其在面对大量需求和复杂逻辑时容易遗漏细节。本文将以电商网…...

深入探索:Scrapy深度爬取策略与实践

标题&#xff1a;深入探索&#xff1a;Scrapy深度爬取策略与实践 引言 在数据驱动的时代&#xff0c;深度爬取成为了获取丰富信息的重要手段。Scrapy&#xff0c;作为一个强大的Python爬虫框架&#xff0c;提供了多种工具和设置来帮助我们实现深度爬取。本文将详细介绍如何在…...

《生成式 AI》课程 第3講:訓練不了人工智慧嗎?你可以訓練你自己

资料来自李宏毅老师《生成式 AI》课程&#xff0c;如有侵权请通知下线 Introduction to Generative AI 2024 Spring 摘要 这一系列的作业是为 2024 年春季的《生成式 AI》课程设计的&#xff0c;共包含十个作业。每个作业都对应一个具体的主题&#xff0c;例如真假难辨的世界…...

如何编译 Cesium 源码

如何编译 Cesium 源码 Cesium 是一个开源的 JavaScript 库&#xff0c;用于构建 3D 地球和地图应用程序。它提供了一套强大的 API 和工具&#xff0c;使开发者能够创建丰富的地理空间应用。本文将指导您如何从 GitHub 下载 Cesium 源码&#xff0c;并在本地进行编译。 TilesB…...

前端开发设计模式——责任链模式

目录 一、定义和特点 1. 定义 2. 特点 二、实现方式 定义抽象处理者&#xff08;Handler&#xff09;类 创建具体处理者&#xff08;ConcreteHandler&#xff09;类 构建责任链 以下是一个用 JavaScript 实现的示例&#xff1a; 三、应用场景 1. 表单验证 2. 请求处…...

JavaWeb--MySQL

1. MySQL概述 首先来了解一下什么是数据库。 数据库&#xff1a;英文为 DataBase&#xff0c;简称DB&#xff0c;它是存储和管理数据的仓库。 像我们日常访问的电商网站京东&#xff0c;企业内部的管理系统OA、ERP、CRM这类的系统&#xff0c;以及大家每天都会刷的头条、抖音…...

Python | Leetcode Python题解之第564题数组嵌套

题目&#xff1a; 题解&#xff1a; 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 简介 接下来一段时间&#xff0c;我会持续发布并完成Spring Boot教程 Spring 被广泛用于创建可扩展的应用程序。对于 Web 应用程序&#xff0c;Spring 提供了 Spring MVC&#xff0c;它是 Spring 的一个广泛使用的模块&#xff0c;用于创建可扩展的 Web 应用程序。…...

Qwen2-VL:发票数据提取、视频聊天和使用 PDF 的多模态 RAG 的实践指南

概述 随着人工智能技术的迅猛发展&#xff0c;多模态模型在各类应用场景中展现出强大的潜力和广泛的适用性。Qwen2-VL 作为最新一代的多模态大模型&#xff0c;融合了视觉与语言处理能力&#xff0c;旨在提升复杂任务的执行效率和准确性。本指南聚焦于 Qwen2-VL 在三个关键领域…...

【安全科普】NUMA防火墙诞生记

一、我为啥姓“NUMA” 随着网络流量和数据包处理需求的指数增长&#xff0c;曾经的我面对“高性能、高吞吐、低延迟”的要求&#xff0c;逐渐变得心有余而力不足。 多CPU技术应运而生&#xff0c;SMP&#xff08;对称多处理&#xff09;和NUMA&#xff08;非一致性内存访问&a…...

机器学习day2-特征工程

四.特征工程 1.概念 一般使用pandas来进行数据清洗和数据处理、使用sklearn来进行特征工程 将任意数据&#xff08;文本或图像等&#xff09;转换为数字特征&#xff0c;对特征进行相关的处理 步骤&#xff1a;1.特征提取&#xff1b;2.无量纲化&#xff08;预处理&#xf…...

沈阳做网站的设计公司哪家好/百度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定理&#xff0c;等价于在$P$进制下每一位分别求组合数最后乘积模$P$。 因为答案为$0$的并不好算&#xff0c;所以可以考虑用$n1$减去其它所有的答案。 那么每一位的组合数都不能是$0$&#xff0c;那么这就保证了$k$的每一位都不大于$n$&#xff0c;所以无需考虑$k\le…...

网站简介 更改/八零云自助建站免费建站平台

if($category->user_id ! auth()->id()){throw new ModelNotFoundException();}...

赚钱网站入口/百度指数官网入口登录

污水中含磷量过高&#xff0c;是不允许排放的&#xff0c;过高的含磷量会导致水体富营养化&#xff0c;从而引发“绿潮”“水华”等水体污染&#xff0c;破坏水生态的自我净化能力&#xff0c;导致水底生物因为缺氧而死亡。所以不管是需要处理才能排放的污水&#xff0c;还是已…...

龙岗附近网站建设/百度网首页官网登录

BMP格式的图片怎在线压缩&#xff0c;大家在上传图片的时候有没有遇到过“因文件太大&#xff0c;上传失败”的问题&#xff0c;图片太大不仅占用空间&#xff0c;而且十分影响传递&#xff0c;那有没有什么解决方法呢&#xff0c;接下来让小编来告诉大家吧。第一、打开电脑&am…...

成都海鸥手表网站/公司在百度怎么推广

诊断Oracle Redo Log性能问题一、Rodo Log性能调整目标&#xff1a;在能够影响Oracle性能的诸多因素中&#xff0c;Redo Log相关的因素从某种程度上可以说是最为重要同时也是最值得关注的。因为在一个OLTP系统中Oracle通过各种技术以及优良的设计&#xff0c;尽量做到将大部分操…...