games101作业7光线追踪 含多线程和微表面提高

对于光线追踪进行综合运用。

光线与三角形求交

其它的emit那些,现在先不用管,后面看看作用是什么。

inline Intersection Triangle::getIntersection(Ray ray)
{Intersection inter;if (dotProduct(ray.direction, normal) > 0)//光线从里面打,无交点return inter;double u, v, t_tmp = 0;Vector3f pvec = crossProduct(ray.direction, e2);double det = dotProduct(e1, pvec);if (fabs(det) < EPSILON)return inter;double det_inv = 1. / det;Vector3f tvec = ray.origin - v0;u = dotProduct(tvec, pvec) * det_inv;if (u < 0 || u > 1)return inter;Vector3f qvec = crossProduct(tvec, e1);v = dotProduct(ray.direction, qvec) * det_inv;if (v < 0 || u + v > 1)return inter;t_tmp = dotProduct(e2, qvec) * det_inv;if(t_tmp<0) return inter;inter.happened=true;inter.coords=ray(t_tmp);inter.normal=normal;inter.distance=t_tmp;inter.obj=this;inter.m=this->m;// TODO find ray triangle intersection// bool happened;// Vector3f coords;// Vector3f tcoords;// Vector3f normal;// Vector3f emit;// double distance;// Object* obj;// Material* m;return inter;
}

判断包围盒与光线是否相交

inline bool Bounds3::IntersectP(const Ray& ray, const Vector3f& invDir,const std::array<int, 3>& dirIsNeg) const
{Vector3f t1=(pMin-ray.origin)*invDir;Vector3f t2=(pMax-ray.origin)*invDir;Vector3f tMin=Vector3f::Min(t1,t2);Vector3f tMax=Vector3f::Max(t1,t2);float tInter=std::max(tMin.x,std::max(tMin.y,tMin.z));float tExit=std::min(tMax.x,std::min(tMax.y,tMax.z));if(tExit>=0&&tInter<=tExit){return true;}// invDir: ray direction(x,y,z), invDir=(1.0/x,1.0/y,1.0/z), use this because Multiply is faster that Division// dirIsNeg: ray direction(x,y,z), dirIsNeg=[int(x>0),int(y>0),int(z>0)], use this to simplify your logic// TODO test if ray bound intersectsreturn false;
}

BVH的查找

Intersection BVHAccel::getIntersection(BVHBuildNode* node, const Ray& ray) const
{Intersection res;if(node==nullptr||!node->bounds.IntersectP(ray,ray.direction_inv,{0,0,0})){return res;}if(node->left==nullptr&&node->right==nullptr){return node->object->getIntersection(ray);}Intersection left=getIntersection(node->left,ray);Intersection right=getIntersection(node->right,ray);res=left.distance<=right.distance?left:right;return res;// TODO Traverse the BVH to find intersection}

实现光线追踪

参考

改动

首先是调用castRay的Renderer::Render(const Scene& scene)

它对同一个pixel采样了16次,求了平均。(这里我不是很懂,理论上不应该是对单个像素的分小像素来取样吗?)

void Renderer::Render(const Scene& scene)
{std::vector<Vector3f> framebuffer(scene.width * scene.height);float scale = tan(deg2rad(scene.fov * 0.5));float imageAspectRatio = scene.width / (float)scene.height;Vector3f eye_pos(278, 273, -800);int m = 0;// change the spp value to change sample ammountint spp = 16;std::cout << "SPP: " << spp << "\n";for (uint32_t j = 0; j < scene.height; ++j) {for (uint32_t i = 0; i < scene.width; ++i) {// generate primary ray directionfloat x = (2 * (i + 0.5) / (float)scene.width - 1) *imageAspectRatio * scale;float y = (1 - 2 * (j + 0.5) / (float)scene.height) * scale;Vector3f dir = normalize(Vector3f(-x, y, 1));for (int k = 0; k < spp; k++){framebuffer[m] += scene.castRay(Ray(eye_pos, dir), 0) / spp;  }m++;}UpdateProgress(j / (float)scene.height);}UpdateProgress(1.f);// save framebuffer to fileFILE* fp = fopen("binary.ppm", "wb");(void)fprintf(fp, "P6\n%d %d\n255\n", scene.width, scene.height);for (auto i = 0; i < scene.height * scene.width; ++i) {static unsigned char color[3];color[0] = (unsigned char)(255 * std::pow(clamp(0, 1, framebuffer[i].x), 0.6f));color[1] = (unsigned char)(255 * std::pow(clamp(0, 1, framebuffer[i].y), 0.6f));color[2] = (unsigned char)(255 * std::pow(clamp(0, 1, framebuffer[i].z), 0.6f));fwrite(color, 1, 3, fp);}fclose(fp);    
}

Object.hpp

增加了获得面积的方式

virtual float getArea()=0;
virtual void Sample(Intersection &pos, float &pdf)=0;
virtual bool hasEmit()=0;

get_random_float()的改动(针对windows)

inline float get_random_float()
{static std::random_device dev;static std::mt19937 rng(dev());static std::uniform_real_distribution<float> dist(0.f, 1.f); // distribution in range [0,1]return dist(rng);
}

本次实验给出的与框架匹配的伪代码

解决

首先我们需要判断射线是否打到物体上,如果没有打到物体上则返回0,0,0.如果打到物体上判断是否是光源,如果是光源,则返回光源的rgb的值,如果不是光源则考虑打到的是物体,则需要使用上面的伪代码来求渲染方程。

如果没直接打到光源,则求渲染方程的步骤为:

  1. 求BRDF
  2. 如果是直接光照,使用sampleLight对光源进行采样求交点射线,求光源的pdf。利用蒙特卡洛积分求直接光照。

如果是间接光照,因为当前只有一条射线,求该射线的随机一个方向,随机的方向与场景中的物体求交点,求得的交点判断是否为光源,不是则计算间接光照。根据俄罗斯赌盘理论进行判断是否求该射线的影响。然后求BRDF,然后递归求该交点物体的间接光照,利用已有的函数求pdf。最后得到间接光照。(一条射线)

Vector3f Scene::castRay(const Ray &ray, int depth) const
{Intersection inter=intersect(ray);Vector3f dir={0.0,0.0,0.0};Vector3f indir=(0.0,0.0,0.0);if(!inter.happened){return dir;}if(inter.m->hasEmission()){return inter.m->getEmission();//如果与之相交的物体材质属于光照,则返回光照}//获取物体的信息// bool happened;// Vector3f coords;// Vector3f tcoords;// Vector3f normal;// Vector3f emit;// double distance;// Object* obj;// Material* m;Vector3f p=inter.coords;Vector3f p_n=inter.normal.normalized();Material* obj_material=inter.m;Vector3f w0=ray.direction;Intersection ans;float pdf_light;sampleLight(ans,pdf_light);//对光照进行采样Vector3f x=ans.coords;Vector3f ws=(x-p).normalized();Vector3f NN=ans.normal.normalized();Vector3f emit=ans.emit;float d=(x-p).norm();Ray objLight(p,ws);Intersection pas=intersect(objLight);//判断物体和光照之间是否有物体遮挡if(abs(pas.distance-ans.distance)<0.001){Vector3f eval=obj_material->eval(w0,ws,p_n);//求PRDFfloat cosinep=dotProduct(NN,-ws);float cosine=dotProduct(p_n,ws);dir+=emit*cosinep*cosine*eval/(d*d)/pdf_light;//没有物体遮挡,计算直接光照}//其他物体间接光照// 其它物体间接光照的方式就是根据欧罗斯赌盘,判断你的这根光线目前是否发射出去。float P_RR=get_random_float();if(P_RR<RussianRoulette){//小于就发射Vector3f wi=obj_material->sample(w0,p_n).normalized();Ray obj_to_obj(p,wi);Intersection obj_to_obj_inter=intersect(obj_to_obj);if(obj_to_obj_inter.happened&&!obj_to_obj_inter.m->hasEmission()){Vector3f eval=obj_material->eval(w0,wi,p_n);float cosine=dotProduct(p_n,wi);float pdf_to_obj=obj_material->pdf(w0,wi,p_n);//求出射方向的概率indir+=castRay(obj_to_obj,depth+1)*eval*cosine/pdf_to_obj/RussianRoulette;//求间接光照}}return indir+dir;//Get x, ws , NN , emit from inter// TO DO Implement Path Tracing Algorithm here
}

一次采样(spp==1)的结果

16次(spp==16)

提高1多线程

这里的思路是多线程去批量处理像素点。提高参考

这里的思路就是简单的对高度进行批量处理,相当于同时处理24个像素点。

void Renderer::Render(const Scene& scene)
{std::vector<Vector3f> framebuffer(scene.width * scene.height);std::atomic_int process=0;//共享变量float scale = tan(deg2rad(scene.fov * 0.5));float imageAspectRatio = scene.width / (float)scene.height;Vector3f eye_pos(278, 273, -800);int m = 0;// change the spp value to change sample ammountint spp = 16;std::cout << "SPP: " << spp << "\n";float thread_num=24;std::thread th[25];int per_height=scene.height/thread_num;std::function<void(uint32_t,uint32_t)> func=[&](uint32_t height,uint32_t height_next){for(uint32_t j=height;j<height_next;j++){for(uint32_t i=0;i<scene.width;i++){float x = (2 * (i + 0.5) / (float)scene.width - 1) *imageAspectRatio * scale;float y = (1 - 2 * (j + 0.5) / (float)scene.height) * scale;Vector3f dir = normalize(Vector3f(-x, y, 1));for(int k=0;k<spp;k++){framebuffer[static_cast<int>(j*scene.width)+i]+=scene.castRay(Ray(eye_pos,dir),0)/spp;}}process++;UpdateProgress(process/(float)scene.height);}};for(int i=0;i<thread_num;i++){th[i]=std::thread(func,per_height*i,per_height*(i+1));}for(int i=0;i<thread_num;i++){th[i].join();}UpdateProgress(1.f);// save framebuffer to fileFILE* fp = fopen("binary.ppm", "wb");(void)fprintf(fp, "P6\n%d %d\n255\n", scene.width, scene.height);for (auto i = 0; i < scene.height * scene.width; ++i) {static unsigned char color[3];color[0] = (unsigned char)(255 * std::pow(clamp(0, 1, framebuffer[i].x), 0.6f));color[1] = (unsigned char)(255 * std::pow(clamp(0, 1, framebuffer[i].y), 0.6f));color[2] = (unsigned char)(255 * std::pow(clamp(0, 1, framebuffer[i].z), 0.6f));fwrite(color, 1, 3, fp);}fclose(fp);    
}

修改cmake文件,链接thread库

cmake_minimum_required(VERSION 3.10)
project(RayTracing)set(CMAKE_CXX_STANDARD 17)add_executable(RayTracing main.cpp Object.hpp Vector.cpp Vector.hpp Sphere.hpp global.hpp Triangle.hpp Scene.cppScene.hpp Light.hpp AreaLight.hpp BVH.cpp BVH.hpp Bounds3.hpp Ray.hpp Material.hpp Intersection.hppRenderer.cpp Renderer.hpp)
find_package(Threads)
target_link_libraries (${PROJECT_NAME} ${CMAKE_THREAD_LIBS_INIT})

同时还需要改变自己虚拟机的运行内核

速度提升的还是比较明显的

提高2 Microfacet

参考1 参考2 计算BRDF

微表面模型(microfacet model) 是一种基于物理的局部光照模型,它假设物体的表面是凹凸不平的,宏观的表面由许多微小的平面(即微表面)构成,光线在每个微平面上发生理想镜面反射或折射。

计算法线分布函数 D

微平面的法线分布函数D(m)描述了微观表面上的表面法线m的统计分布。(法线分布函数:某个点上,法线指向指定方向上的概率。) 它表示在单位面积上,法线向量与半程向量 hhh 一致的微表面面积的比例。

业界较为主流的法线分布函数是GGX(Trowbridge-Reitz),因为具有更好的高光长尾。

  • α为粗糙度∈[0,1]
  • n为宏观平面的法线
  • h为微观平面法线,即半程向量(入射光线和出射光线的)

当α=1时,D=1/π,光线向半球面均匀发散

当α=0时,只有当n=h时,D≠=0

代码

float D_GGX(const Vector3f& n,const Vector3f& h,float rough){float a2=roughness*roughness;float _p=M_PI*pow(pow(dotProduct(normalize(N),normalize(H)),2.0)*(a2-1.0f)+1.0f,2.0f);float _pas=std::max(_p,0.0000001f);float D_res=a2/_pas;return D_res;
}

几何函数

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

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

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

G(n,v,l,k)返回遮蔽阴影函数的计算结果。

  • N为宏观表面法线
  • V为光线反射方向(可理解为视口的方向)
  • L为光线进入的反方向(可理解为光源的方向)
  • roughness为粗糙度。

实际上就是分别使用入射和出射方向求Gdirection然后相乘

float GeometrySchlickGGX(const Vector3f& normal,const Vector3f& v,const float k){Vector3f _normal=normalize(normal);Vector3f _v=normalize(v);float _pas=std::max(dotProduct(_normal,_v),0.0f);float _mpas=_pas*(1.0f-k)+k;return _pas/_mpas;
}
float GeometrySmith(const Vector3f& n,const Vector3f& wi,const Vector3f wo,float rough){float k=(rough+1.0f)*(rough+1.0f)/8.0f;return GeometrySchlickGGX(n,wi,k)*GeometrySchlickGGX(n,wo,k);
}

Fresnel方程 F(菲涅尔项)

float Fresnel(const float& n1,const float& n2,const Vector3f& wi,const Vector3f n,float& F){float r0=pow((n1-n2)/(n1+n2),2.0f);float _pas=std::max(dotProduct(normalize(wi),normalize(n)),0.0f);return r0+(1-r0)*pow((1-_pas),5.0f);
}

但是在工程中,实现的是完整版的

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 lawfloat sint = etai / etat * sqrtf(std::max(0.f, 1 - cosi * cosi));// Total internal reflectionif (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;}

计算微表面材质

为了能量守恒,漫反射归到折射光照中,高光为ks也就是镜面反射。

Vector3f Material::eval(const Vector3f &wi, const Vector3f &wo, const Vector3f &N){switch(m_type){case DIFFUSE:{// calculate the contribution of diffuse   modelfloat cosalpha = dotProduct(N, wo);if (cosalpha > 0.0f) {Vector3f diffuse = Kd / M_PI;return diffuse;}elsereturn Vector3f(0.0f);break;}case Microfacet://微表面材质{float cosalpha = dotProduct(N, wo);if(cosalpha>0.0f){float F;Vector3f specular;specular=caculate(wo,wi,N,F);Vector3f diffuse=1.0f/M_PI;float _kd=1-F;return specular*Ks+_kd*Kd*diffuse;}else{return Vector3f(0.0f);}break;}}
}

其它在Material中用到diffuse材质的部分,也需要加上微表面材质

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;elsereturn 0.0f;break;}}
}
Vector3f Material::sample(const Vector3f &wi, const Vector3f &N){switch(m_type){case DIFFUSE:case Microfacet:{// uniform sample on the hemispherefloat x_1 = get_random_float(), x_2 = get_random_float();float z = std::fabs(1.0f - 2.0f * x_1);float r = std::sqrt(1.0f - z * z), phi = 2 * M_PI * x_2;Vector3f localRay(r*std::cos(phi), r*std::sin(phi), z);return toWorld(localRay, N);break;}}
}
Material* red = new Material(Microfacet, Vector3f(0.0f));red->Kd = Vector3f(0.63f, 0.065f, 0.05f);Material* green = new Material(Microfacet, Vector3f(0.0f));green->Kd = Vector3f(0.14f, 0.45f, 0.091f);Material* white = new Material(Microfacet, Vector3f(0.0f));white->Kd = Vector3f(0.725f, 0.71f, 0.68f);Material* light = new Material(Microfacet, (8.0f * Vector3f(0.747f+0.058f, 0.747f+0.258f, 0.747f) + 15.6f * Vector3f(0.740f+0.287f,0.740f+0.160f,0.740f) + 18.4f *Vector3f(0.737f+0.642f,0.737f+0.159f,0.737f)));light->Kd = Vector3f(0.65f);

换成兔子模型 参考

int main(int argc, char** argv)
{// Change the definition here to change resolutionScene scene(784, 784);Material* red = new Material(DIFFUSE, Vector3f(0.0f));red->Kd = Vector3f(0.63f, 0.065f, 0.05f);Material* green = new Material(DIFFUSE, Vector3f(0.0f));green->Kd = Vector3f(0.14f, 0.45f, 0.091f);Material* white = new Material(DIFFUSE, Vector3f(0.0f));white->Kd = Vector3f(0.725f, 0.71f, 0.68f);Material* light = new Material(DIFFUSE, (8.0f * Vector3f(0.747f+0.058f, 0.747f+0.258f, 0.747f) + 15.6f * Vector3f(0.740f+0.287f,0.740f+0.160f,0.740f) + 18.4f *Vector3f(0.737f+0.642f,0.737f+0.159f,0.737f)));light->Kd = Vector3f(0.65f);Material* whitem = new Material(Microfacet,Vector3f(0.0f));whitem->Ks=Vector3f(0.45f,0.45f,0.45f);whitem->Kd=Vector3f(0.3f,0.3f,0.25f);MeshTriangle bunny("../models/bunny/bunny.obj",whitem);MeshTriangle floor("../models/cornellbox/floor.obj", white);// MeshTriangle shortbox("../models/cornellbox/shortbox.obj", white);// MeshTriangle tallbox("../models/cornellbox/tallbox.obj", white);MeshTriangle left("../models/cornellbox/left.obj", red);MeshTriangle right("../models/cornellbox/right.obj", green);MeshTriangle light_("../models/cornellbox/light.obj", light);scene.Add(&floor);// scene.Add(&shortbox);// scene.Add(&tallbox);scene.Add(&left);scene.Add(&right);scene.Add(&light_);scene.Add(&bunny);scene.buildBVH();Renderer r;auto start = std::chrono::system_clock::now();r.Render(scene);auto stop = std::chrono::system_clock::now();std::cout << "Render complete: \n";std::cout << "Time taken: " << std::chrono::duration_cast<std::chrono::hours>(stop - start).count() << " hours\n";std::cout << "          : " << std::chrono::duration_cast<std::chrono::minutes>(stop - start).count() << " minutes\n";std::cout << "          : " << std::chrono::duration_cast<std::chrono::seconds>(stop - start).count() << " seconds\n";return 0;
}

但是这样是看不到兔子的

需要对兔子进行位置和大小变换

只需要将兔子每个顶点进行放缩和位移

MeshTriangle bunny("../models/bunny/bunny.obj",whitem,Vector3f(2000.0f),Vector3f(300.0f,0.0f,300.0f));
MeshTriangle(const std::string& filename, Material *mt = new Material(),Vector3f Scale=Vector3f(1.0f,1.0f,1.0f),Vector3f move=Vector3f(0.0f,0.0f,0.0f)){objl::Loader loader;loader.LoadFile(filename);area = 0;m = mt;assert(loader.LoadedMeshes.size() == 1);auto mesh = loader.LoadedMeshes[0];Vector3f min_vert = Vector3f{std::numeric_limits<float>::infinity(),std::numeric_limits<float>::infinity(),std::numeric_limits<float>::infinity()};Vector3f max_vert = Vector3f{-std::numeric_limits<float>::infinity(),-std::numeric_limits<float>::infinity(),-std::numeric_limits<float>::infinity()};for (int i = 0; i < mesh.Vertices.size(); i += 3) {std::array<Vector3f, 3> face_vertices;for (int j = 0; j < 3; j++) {auto vert = Vector3f(mesh.Vertices[i + j].Position.X,mesh.Vertices[i + j].Position.Y,mesh.Vertices[i + j].Position.Z);vert=vert*Scale+move;face_vertices[j] = vert;min_vert = Vector3f(std::min(min_vert.x, vert.x),std::min(min_vert.y, vert.y),std::min(min_vert.z, vert.z));max_vert = Vector3f(std::max(max_vert.x, vert.x),std::max(max_vert.y, vert.y),std::max(max_vert.z, vert.z));}triangles.emplace_back(face_vertices[0], face_vertices[1],face_vertices[2], mt);}bounding_box = Bounds3(min_vert, max_vert);std::vector<Object*> ptrs;for (auto& tri : triangles){ptrs.push_back(&tri);area += tri.area;}bvh = new BVHAccel(ptrs);}

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

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

相关文章

[Shell编程学习路线]——深入理解Shell编程中的变量(理论与实例)

&#x1f3e1;作者主页&#xff1a;点击&#xff01; &#x1f6e0;️Shell编程专栏&#xff1a;点击&#xff01; ⏰️创作时间&#xff1a;2024年6月12日11点40分 &#x1f004;️文章质量&#xff1a;95分 文章目录 ————前言———— 1 自定义变量 &#x1fae0;…

Zynq学习笔记--AXI4-Stream到视频输出IP是如何工作的?

目录 1. 简介 2. 原理详解 2.1 示例工程 2.2 AXI4-Stream to Video Out 3. Master/Slave Timing Mode 3.1 Slave Timing Mode 3.2 Master Timing Mode 4. 总结 1. 简介 本文主要介绍了 AXI4-Stream 到视频输出 的内容。其中&#xff0c;示例工程展示了一个具体的设计&…

GitLab教程(五):高效的工作模式——Feature Branching

文章目录 1.什么是Feature Branching2.Feature Branching的Git实践 1.什么是Feature Branching 特性分支&#xff08;Feature Branching&#xff09;是一种软件开发工作流&#xff0c;尤其在使用Git或其他版本控制系统时被广泛采用。这种策略鼓励开发者为每一个新功能、改进或…

推荐一款好用的读论文软件操作方法

步骤&#xff1a; 1. 使用一译 —— 文档和论文翻译、对照阅读、讨论和社区 2.上传自己想要翻译的论文即可。 示例 Planing论文双语翻译 1.1 Parting with Misconceptions about Learning-based Vehicle Motion Planning 中英文对照阅读 1.2 Rethinking Imitation-based Pl…

SCT82A32 是一款 100V 电压模式控制同步降压控制器

主要特征 ◦ 5.5V-100V 宽输入范围 ◦ 0.8V-60V 可调输出电压 ◦ 0.8V1% 参考电压 ◦ 最低占空比下的40ns 最小 tON ◦ 最高占空比下的150ns 最小 tOFF • 100 KHz 到 1.2 MHz 开关频率 ◦ 时钟同步输入/输出功能 ◦ 可选择二极管仿真或 FPWM • 7.5V 门极驱动器 ◦ 2.3A …

Spring Cloud Gateway 详解:构建高效的API网关解决方案

Spring Cloud Gateway 详解&#xff1a;构建高效的API网关解决方案 Spring Cloud Gateway 是 Spring Cloud 生态系统中用于构建 API 网关的核心组件。它基于 Spring WebFlux 构建&#xff0c;旨在提供简单且有效的方式来路由和增强 API 请求。以下是 Spring Cloud Gateway 的详…

【iOS】YYModel源码阅读笔记

文章目录 前言一、JSON转换库对比二、YYModel性能优化三、YYModel的使用四、架构分析YYClassInfo 剖析 五、流程剖析转换前准备工作 – 将JSON统一成NSDictionary将NSDictionary 转换为Model对象提取Model信息使用NSDictionary的数据填充Model 总结 前言 先前写了JSONModel的源…

如何计算可截素数

什么是可截素数&#xff1f; 它本身是一个素数&#xff0c;如果从左往右逐一截去数字&#xff0c;剩下的仍然都是素数&#xff0c;如果从右往左逐一截去数字&#xff0c;剩下的也仍然都是素数。 例如&#xff1a;3797就是一个可截素数。 从左往右截去数字&#xff1a;797&a…

利用three-csg-ts对做物体交互式挖洞

默认物体均为居中&#xff0c;如果指定位置没有发生偏移&#xff0c;可能是因为在执行布尔操作之前没有正确设置变换。确保在进行布尔运算之前应用所有必要的变换。以下是经过修正的完整代码示例&#xff0c;它会确保圆柱正确旋转并与盒子进行 CSG 操作。 安装依赖 首先&…

如何实现网络隔离后,军工单位内网数据导出的安全性?

在现代信息化战争中&#xff0c;军工单位在信息安全方面的需求尤为突出。通常会采用物理隔离&#xff0c;将网络隔离成内网和外网&#xff0c;防止外部网络的恶意入侵和数据窃取。隔离后的数据仍存在内外网交换的需求&#xff0c;即涉及到内网数据导出&#xff0c;因此每日会面…

盒马鲜生礼品卡如何使用?

盒马鲜生的礼品卡除了在门店用以外&#xff0c;还有什么用处啊 毕竟家附近的盒马距离都太远了&#xff0c;好多卡最后都闲置下来了&#xff0c;而且以前都不知道盒马卡还会过期&#xff0c;浪费了好多 还好最近发现了 盒马鲜生礼品卡现在也能在收卡云上兑现了&#xff0c;而且…

深入解析 Spring Cloud Sentinel:分布式系统流量控制与熔断降级的全面指南

&#x1f4e2;&#x1f4e2;&#x1f4e2; 深入解析 Spring Cloud Sentinel&#xff1a;分布式系统流量控制与熔断降级的全面指南 Spring Cloud Sentinel 是阿里巴巴开源的一款强大的分布式系统流量防卫组件&#xff0c;专为微服务架构设计&#xff0c;提供流量控制、熔断降级…

watcher学习小结

架构 主要是watcher-api&#xff0c;watcher-applier&#xff0c;watcher-decision-engine watcher-applier watcher-decision-engine 将DecisionEngineManager和DecisionEngineSchedulingService封装到oslo_service&#xff0c;然后调service的launch_service&#xff0c;实…

NetSuite ERP项目中非批次物料—批次物料数据转换流程

最近在刚结束的项目上也再次碰到了非批次物料转换为批次物料的操作&#xff0c;因此也想把我们在处理数据流程中的心得写出来&#xff0c;以便复盘与总结&#xff0c;也分享给各位。 整体的步骤我们可分为准备工作&#xff0c;调整工作以及检查工作&#xff1a; 准备工作 主…

抖店退款退货率太高,平台也不帮助商家,快做不下去了怎么办?

我是王路飞。 现在很多商家对抖店的评价是&#xff1a;比拼多多还狠&#xff0c;动不动就扣保证金&#xff0c;退款率太高&#xff0c;而平台一边倒站买家&#xff0c;要是再遇到个别发疯的买家&#xff0c;商家真的很无助。 其实关于抖店退款退货率高、平台也不站在商家这一…

编译结果处理的shell脚本

#!/bin/bash WEB"web" DIST"dist" RED\033[0:31m GREEN\033[0;32m NC\033[0m #生产打包传参 BUILD"b" if [ -e ${WEB} ];then#删历史文件rm -r ${WEB}rm ${WEB}.zip fi #编辑文件 npm run build #检查构建是否成功 if[ -e ${DIST} ];then#改名mv…

分布式事务的八种方案解析(1)

针对不同的分布式场景业界常见的解决方案有2PC、TCC、可靠消息最终一致性、最大努力通知等方案&#xff0c;以下总结8 种常见的解决方案&#xff0c;帮助大家在实际的分布式系统中更好地运用事务。 1.2PC 二阶段提交协议&#xff08;Two-phase commit protocol&#xff09;&…

好用的视频压缩软件

在当今数字化时代&#xff0c;视频已成为我们日常生活中不可或缺的一部分。无论是工作、学习还是娱乐&#xff0c;视频都扮演着重要的角色。视频的存储空间也越来越大&#xff0c;这给我们的设备存储带来了不小的挑战。因此&#xff0c;学习如何将视频压缩小点成为了一项实用的…

叁[3],VM二次开发异常处理

1&#xff0c;开发环境 VS2022/WPF/.NetFramework4.8 VM4.2 2&#xff0c;"模块状态0&#xff0c;错误码10100005&#xff0c;错误信息:模块与平台不匹配" 现象描述&#xff1a; 1&#xff0c;WPF/NetFramework项目中打开方案&#xff0c;对工具做模板&#xff0c…

开源可视化表单可以用在哪些行业中?

很多客户朋友会询问我们&#xff0c;什么样的行业可以使用低代码技术平台及开源可视化表单&#xff1f;其实&#xff0c;随着社会的进步和发展&#xff0c;很多中小型企业都希望通过低代码技术平台能够让企业实现提质增效的目的&#xff0c;也想借助它的优势特点进入流程化办公…