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

GAMES101:现代计算机图形学入门-作业五

作业五

这次作业给了许多脚本,我们现在可以把每个脚本的代码逐行细细分析一下。

main.cpp

#include "Scene.hpp"
#include "Sphere.hpp"
#include "Triangle.hpp"
#include "Light.hpp"
#include "Renderer.hpp"// In the main function of the program, we create the scene (create objects and lights)
// as well as set the options for the render (image width and height, maximum recursion
// depth, field-of-view, etc.). We then call the render function().
int main()
{Scene scene(1280, 960);//设置场景屏幕尺寸auto sph1 = std::make_unique<Sphere>(Vector3f(-1, 0, -12), 2);//生成小球1//(Vector3f(-1, 0, -12), 2)中分别代表球心位置与半径sph1->materialType = DIFFUSE_AND_GLOSSY;//材质选择漫反射——Glossysph1->diffuseColor = Vector3f(0.6, 0.7, 0.8);//设置color的RGB值auto sph2 = std::make_unique<Sphere>(Vector3f(0.5, -0.5, -8), 1.5);//生成小球2sph2->ior = 1.5;//ior代表材质的折射率sph2->materialType = REFLECTION_AND_REFRACTION;//材质选择反射_折射型scene.Add(std::move(sph1));scene.Add(std::move(sph2));//将小球添加到场景中Vector3f verts[4] = {{-5,-3,-6}, {5,-3,-6}, {5,-3,-16}, {-5,-3,-16}};//给了四个顶点uint32_t vertIndex[6] = {0, 1, 3, 1, 2, 3};//给了六个序号Vector2f st[4] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}};//给了四个纹理坐标auto mesh = std::make_unique<MeshTriangle>(verts, vertIndex, 2, st);//生成了一个MeshTriangle类型的对象,接受参数:verts:顶点坐标数组,vertIndex:顶点索引数组,2:两个三角形,st:纹理坐标数组mesh->materialType = DIFFUSE_AND_GLOSSY;//同样定义材质scene.Add(std::move(mesh));//场景中添加这个网格scene.Add(std::make_unique<Light>(Vector3f(-20, 70, 20), 0.5));scene.Add(std::make_unique<Light>(Vector3f(30, 50, -12), 0.5));   //添加了两个点光源 Renderer r;//实例化渲染器r.Render(scene);//渲染器对场景进行渲染return 0;
}

每句代码的意义如注释所示,我们生成了两个材质不同的小球,一个是漫反射材质一个是反射材质;接着给定一系列顶点坐标与纹理坐标后,我们定义一个网格并添加到场景中,接着向场景中添加两个点光源,最后调用渲染器进行渲染。

这个脚本比较有意思的点在于std命名空间里的make_unique与move:

make_unique的作用在于创建一个unique_ptr指针,这种指针可以独享一个对象的权限,且智能地在超出作用域后释放多余的内存。

move的作用在于将对象转换成右值引用,这样可以将一个对象的所有权全部转移到另一个指针上而省去了拷贝的麻烦。

有关move与右值引用的概念相对来说比较复杂,详细地可以看这里:C++右值引用和std::move 阅读笔记 - 知乎 (zhihu.com)

 Renderer.cpp

这个脚本比较长,我们分开慢慢说:

#include <fstream>
#include "Vector.hpp"
#include "Renderer.hpp"
#include "Scene.hpp"
#include <optional>inline float deg2rad(const float &deg)//将度(degree)转换为弧度制(radians)
{ return deg * M_PI/180.0; }// Compute reflection direction
Vector3f reflect(const Vector3f &I, const Vector3f &N)//为我们写好了反射函数
{return I - 2 * dotProduct(I, N) * N;
}// [comment]
// Compute refraction direction using Snell's law
//
// We need to handle with care the two possible situations:
//
//    - When the ray is inside the object
//
//    - When the ray is outside.
//
// If the ray is outside, you need to make cosi positive cosi = -N.I
//
// If the ray is inside, you need to invert the refractive indices and negate the normal N
// [/comment]
Vector3f refract(const Vector3f &I, const Vector3f &N, const float &ior)//折射
{float cosi = clamp(-1, 1, dotProduct(I, N));float etai = 1, etat = ior;Vector3f n = N;if (cosi < 0) { cosi = -cosi; } else { std::swap(etai, etat); n= -N; }float eta = etai / etat;float k = 1 - eta * eta * (1 - cosi * cosi);return k < 0 ? 0 : eta * I + (eta * cosi - sqrtf(k)) * n;
}// [comment]
// Compute Fresnel equation
//
// \param I is the incident view direction
//
// \param N is the normal at the intersection point
//
// \param ior is the material refractive index
// [/comment]
float fresnel(const Vector3f &I, const Vector3f &N, const float &ior)//菲涅尔项
{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) {return 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));return (Rs * Rs + Rp * Rp) / 2;}// As a consequence of the conservation of energy, transmittance is given by:// kt = 1 - kr;

首先是一个内联的函数,用于将平时数学上讨论的度(degree)转换成弧度制(radians),有关内联函数(inline),大体上我们可以理解为我们稍微牺牲一点存储空间将一个可能会高度调用的小体量函数内嵌至调用点,这样可以提高这个函数调用的效率。主要用于针对哪些可能调用函数花费的时间大于执行函数的函数使用inline关键字。

接着是一个反射函数,本质上他的逻辑就是反射定理:入射角等于出射角,但是怎么得到这个代码的算式的呢?

I代表入射角,N代表法线方向

大体上就是如此~

接着是折射函数的部分,同样参数是入射角和法线,还多了一个折射率。

首先计算入射角和法线的夹角的余弦值且利用clamp函数将这个余弦值限制在[-1,1]之间。接着我们需要对这个余弦值进行一个判断:如果这个值小于0,证明光线是从外部射入内部(入射角大于180度证明入射角是与法线方向相悖,也就是从外部射向内部);所以如果我们法线这个余弦值大于0的话,我们需要转换法线的方向。

接着是对是否发生全反射的判断:我们需要计算两种介质的折射率之比,并利用折射定理:

这也就是上述代码的由来。

接着是菲涅尔项的推导:

综上所述,其实我们只用把入射介质和反射介质的n1,n2就可以得到菲涅尔项。

// [comment]
// Returns true if the ray intersects an object, false otherwise.
//
// \param orig is the ray origin
// \param dir is the ray direction
// \param objects is the list of objects the scene contains
// \param[out] tNear contains the distance to the cloesest intersected object.
// \param[out] index stores the index of the intersect triangle if the interesected object is a mesh.
// \param[out] uv stores the u and v barycentric coordinates of the intersected point
// \param[out] *hitObject stores the pointer to the intersected object (used to retrieve material information, etc.)
// \param isShadowRay is it a shadow ray. We can return from the function sooner as soon as we have found a hit.
// [/comment]
std::optional<hit_payload> trace(const Vector3f &orig, const Vector3f &dir,const std::vector<std::unique_ptr<Object> > &objects)//光线追踪函数//返回的类型std::optional<hit_payload>代表可能返回一个 hit_payload 对象,也可能返回 std::nullopt(表示没有交点)
{                                                            //接受的参数包括光源的起点,光的方向,以及场景内物体float tNear = kInfinity;std::optional<hit_payload> payload;//optional类型满足一个函数返回值或者不返回值,避免了裸指针的情况for (const auto & object : objects){float tNearK = kInfinity;//初始化当前对象的最近交点距离为无穷大uint32_t indexK;//初始化当前对象的交点索引Vector2f uvK;//初始化当前对象的交点纹理坐标if (object->intersect(orig, dir, tNearK, indexK, uvK) && tNearK < tNear)//判断是否有交点,接受的参数包括光源坐标,光线方向,交点距离,交点索引,纹理坐标,判断目前的交点距离是否小于无穷大{payload.emplace();//填充payloadpayload->hit_obj = object.get();//完善payload的objectpayload->tNear = tNearK;//完善payload的交点距离payload->index = indexK;//完善payload的交点序号payload->uv = uvK;//完善payload的纹理坐标tNear = tNearK;//重新修正交点距离至无穷远}}return payload;
}

接着是有关追踪部分的代码:std::optional代表的是该类型的返回值可以有也可以是没有,从而避免裸指针的情况。

这段代码的大意我都写在了注释上,这段代码的意义就是判断有没有交点,有的话读取诸如交点距离等数据给到我们的payload并返回。但是我这里补充一下几个里面的定义:

struct hit_payload 
{float tNear;uint32_t index;Vector2f uv;Object* hit_obj;
};class Renderer//渲染器类定义
{
public:void Render(const Scene& scene);private:
};

hit_payload是一个结构体,包含的成员变量有:交点距离,交点序号,纹理坐标以及产生碰撞的物体的指针。如果我们接着展开Object的定义的话:

#pragma once#include "Vector.hpp"
#include "global.hpp"class Object//物体类定义
{
public:Object()//构造函数: materialType(DIFFUSE_AND_GLOSSY)//材质类型:漫反射与, ior(1.3)//折射率, Kd(0.8)//漫反射系数, Ks(0.2)//镜面反射系数, diffuseColor(0.2)//漫反射颜色, specularExponent(25)//高光指数{}virtual ~Object() = default;//虚析构函数=defaultvirtual bool intersect(const Vector3f&, const Vector3f&, float&, uint32_t&, Vector2f&) const = 0;//定义纯虚函数便于派生类重写(Override)//这里的const与= 0是分开写的,const用来修饰函数,= 0 是纯虚函数的一部分virtual void getSurfaceProperties(const Vector3f&, const Vector3f&, const uint32_t&, const Vector2f&, Vector3f&,Vector2f&) const = 0;virtual Vector3f evalDiffuseColor(const Vector2f&) const{return diffuseColor;}// material propertiesMaterialType materialType;//材质类型float ior;//折射率float Kd, Ks;//漫反射系数和镜面反射系数Vector3f diffuseColor;//漫反射颜色float specularExponent;//高光指数
};

除了最基础的成员变量以及无参构造函数,虚析构函数以外,Object类中还定义了一个判断是否有交点的函数intersect与一个获取物体表面材质属性的函数getSurfaceProperties与一个返回漫反射颜色的函数。前两个是纯虚函数(代表性的=0),而最后一个函数则是已经提供了默认的实现(返回漫反射颜色)。

// [comment]
// Implementation of the Whitted-style light transport algorithm (E [S*] (D|G) L)
//
// This function is the function that compute the color at the intersection point(计算在交点的颜色)
// of a ray defined by a position and a direction. Note that thus function is recursive (it calls itself)(递归的).
//
// If the material of the intersected object is either reflective or reflective and refractive,
// then we compute the reflection/refraction direction and cast two new rays into the scene
// by calling the castRay() function recursively. When the surface is transparent, we mix
// the reflection and refraction color using the result of the fresnel equations (it computes
// the amount of reflection and refraction depending on the surface normal, incident view direction
// and surface refractive index).
//
// If the surface is diffuse/glossy we use the Phong illumation model to compute the color
// at the intersection point.
// [/comment]
Vector3f castRay(const Vector3f &orig, const Vector3f &dir, const Scene& scene,int depth)//接受的参数包括光源坐标,光线方向,场景以及递归深度(防止无限递归)
{if (depth > scene.maxDepth) {return Vector3f(0.0,0.0,0.0);}//如果递归深度超过了最大深度则返回黑色表示已经递归超过了最大深度Vector3f hitColor = scene.backgroundColor;//获取场景的背景色if (auto payload = trace(orig, dir, scene.get_objects()); payload)//调用trace函数,如果光线与物体产生交点则会返回payload结构体{Vector3f hitPoint = orig + dir * payload->tNear;//获取交点坐标,计算方式为光源点加上方向乘以payload的交点距离与方向的乘积Vector3f N; // normal法线Vector2f st; // st coordinates纹理坐标payload->hit_obj->getSurfaceProperties(hitPoint, dir, payload->index, payload->uv, N, st);//调用switch (payload->hit_obj->materialType) {//根据产生交点的物体的材质进行分类讨论:case REFLECTION_AND_REFRACTION://反射_折射材质{Vector3f reflectionDirection = normalize(reflect(dir, N));//获取反射方向单位向量(normalize)Vector3f refractionDirection = normalize(refract(dir, N, payload->hit_obj->ior));//获取折射反向单位向量Vector3f reflectionRayOrig = (dotProduct(reflectionDirection, N) < 0) ?//通过判断反射方向与法线向量的夹角余弦值来判断出射光是从表面的内部还是外部发射出去hitPoint - N * scene.epsilon :hitPoint + N * scene.epsilon;//进行一定程度的起点移动防止出现自交问题Vector3f refractionRayOrig = (dotProduct(refractionDirection, N) < 0) ?hitPoint - N * scene.epsilon :hitPoint + N * scene.epsilon;Vector3f reflectionColor = castRay(reflectionRayOrig, reflectionDirection, scene, depth + 1);Vector3f refractionColor = castRay(refractionRayOrig, refractionDirection, scene, depth + 1);//进行递归且递归深度加一float kr = fresnel(dir, N, payload->hit_obj->ior);//通过计算菲涅尔项得到反射与折射的比值hitColor = reflectionColor * kr + refractionColor * (1 - kr);//得到颜色break;}case REFLECTION://反射材质{float kr = fresnel(dir, N, payload->hit_obj->ior);Vector3f reflectionDirection = reflect(dir, N);Vector3f reflectionRayOrig = (dotProduct(reflectionDirection, N) < 0) ?hitPoint + N * scene.epsilon :hitPoint - N * scene.epsilon;hitColor = castRay(reflectionRayOrig, reflectionDirection, scene, depth + 1) * kr;break;}default://默认即Phong模型,由漫反射与高光组成{// [comment]// We use the Phong illumation model int the default case. The phong model// is composed of a diffuse and a specular reflection component.// [/comment]Vector3f lightAmt = 0, specularColor = 0;//定义光照强度和高光颜色Vector3f shadowPointOrig = (dotProduct(dir, N) < 0) ?//计算影子的位置,对于夹角余弦的判断同前文一样hitPoint + N * scene.epsilon :hitPoint - N * scene.epsilon;// [comment]// Loop over all lights in the scene and sum their contribution up// We also apply the lambert cosine law// [/comment]for (auto& light : scene.get_lights()) {Vector3f lightDir = light->position - hitPoint;//通过向量相减的性质,用光源位置减去交点得到光源射向交点的光线向量// square of the distance between hitPoint and the lightfloat lightDistance2 = dotProduct(lightDir, lightDir);//计算光线向量的模长平方lightDir = normalize(lightDir);//归一化float LdotN = std::max(0.f, dotProduct(lightDir, N));//判断入射光是否从表面外部射入(是否在影子里)// is the point in shadow, and is the nearest occluding object closer to the object than the light itself?auto shadow_res = trace(shadowPointOrig, lightDir, scene.get_objects());bool inShadow = shadow_res && (shadow_res->tNear * shadow_res->tNear < lightDistance2);lightAmt += inShadow ? 0 : light->intensity * LdotN;//如果该光线不在影子里,我们将光的强度(intensity)加给lightAmtVector3f reflectionDirection = reflect(-lightDir, N);//反射角等于入射角specularColor += powf(std::max(0.f, -dotProduct(reflectionDirection, dir)),//powf用于计算幂//这里用来计算高光颜色payload->hit_obj->specularExponent) * light->intensity;}hitColor = lightAmt * payload->hit_obj->evalDiffuseColor(st) * payload->hit_obj->Kd + specularColor * payload->hit_obj->Ks;break;}}}return hitColor;
}

我们的castRay函数可谓是重中之重了,他的内容就是ws风格的光线追踪的内容。

(作业代码1处)

// [comment]
// The main render function. This where we iterate over all pixels in the image, generate
// primary rays and cast these rays into the scene. The content of the framebuffer is
// saved to a file.
// [/comment]
void Renderer::Render(const Scene& scene)//继承自Render的Renderer
{std::vector<Vector3f> framebuffer(scene.width * scene.height);//帧缓存,容器大小初始为屏幕宽乘以高float scale = std::tan(deg2rad(scene.fov * 0.5f));//定义尺度float imageAspectRatio = scene.width / (float)scene.height;//定义宽高比// Use this variable as the eye position to start your rays.Vector3f eye_pos(0);//相机观察的坐标int m = 0;for (int j = 0; j < scene.height; ++j){for (int i = 0; i < scene.width; ++i){// generate primary ray direction// TODO: Find the x and y positions of the current pixel to get the direction// vector that passes through it.// Also, don't forget to multiply both of them with the variable *scale*, and// x (horizontal) variable with the *imageAspectRatio*            float x = (2.0f * (float(i) + 0.5f) / scene.width - 1.0f) * scale * imageAspectRatio;float y = (1.0f - 2.0f * (float(j) + 0.5f) / scene.height) * scale;Vector3f dir = Vector3f(x, y, -1); // Don't forget to normalize this direction!framebuffer[m++] = castRay(eye_pos, dir, scene, 0);}UpdateProgress(j / (float)scene.height);}// 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] = (char)(255 * clamp(0, 1, framebuffer[i].x));color[1] = (char)(255 * clamp(0, 1, framebuffer[i].y));color[2] = (char)(255 * clamp(0, 1, framebuffer[i].z));fwrite(color, 1, 3, fp);}fclose(fp);    
}

Triangle.hpp

(作业代码2处)

#pragma once#include "Object.hpp"#include <cstring>bool rayTriangleIntersect(const Vector3f& v0, const Vector3f& v1, const Vector3f& v2, const Vector3f& orig,const Vector3f& dir, float& tnear, float& u, float& v)//判断光线与三角形是否有交点
{   //vo,v1,v2代表三个顶点,orig代表光源坐标,dir代表光线方向,tnear代表交点距离,u,v代表重心坐标系中其二的系数// TODO: Implement this function that tests whether the triangle// that's specified bt v0, v1 and v2 intersects with the ray (whose// origin is *orig* and direction is *dir*)// Also don't forget to update tnear, u and v.Vector3f E1 = v1 - v0;Vector3f E2 = v2 - v0;Vector3f S = orig - v0;Vector3f S1 = crossProduct(dir, E2);//叉乘Vector3f S2 = crossProduct(S, E1);float n = 1.0f / dotProduct(S1, E1);Vector3f res(dotProduct(S2, E2), dotProduct(S1, S), dotProduct(S2, dir));res = n * res;tnear = res.x;u = res.y;v = res.z;if (tnear > 0.f && 1 - u - v >= 0.f && u >= 0.f && v >= 0.f)//判断重心坐标系的三个系数为非负return true;elsereturn false;}class MeshTriangle : public Object//继承自Object
{
public:MeshTriangle(const Vector3f* verts, const uint32_t* vertsIndex, const uint32_t& numTris, const Vector2f* st){uint32_t maxIndex = 0;for (uint32_t i = 0; i < numTris * 3; ++i)if (vertsIndex[i] > maxIndex)maxIndex = vertsIndex[i];maxIndex += 1;vertices = std::unique_ptr<Vector3f[]>(new Vector3f[maxIndex]);memcpy(vertices.get(), verts, sizeof(Vector3f) * maxIndex);vertexIndex = std::unique_ptr<uint32_t[]>(new uint32_t[numTris * 3]);memcpy(vertexIndex.get(), vertsIndex, sizeof(uint32_t) * numTris * 3);numTriangles = numTris;stCoordinates = std::unique_ptr<Vector2f[]>(new Vector2f[maxIndex]);memcpy(stCoordinates.get(), st, sizeof(Vector2f) * maxIndex);}bool intersect(const Vector3f& orig, const Vector3f& dir, float& tnear, uint32_t& index,Vector2f& uv) const override{bool intersect = false;for (uint32_t k = 0; k < numTriangles; ++k){const Vector3f& v0 = vertices[vertexIndex[k * 3]];const Vector3f& v1 = vertices[vertexIndex[k * 3 + 1]];const Vector3f& v2 = vertices[vertexIndex[k * 3 + 2]];float t, u, v;if (rayTriangleIntersect(v0, v1, v2, orig, dir, t, u, v) && t < tnear){tnear = t;uv.x = u;uv.y = v;index = k;intersect |= true;}}return intersect;}void getSurfaceProperties(const Vector3f&, const Vector3f&, const uint32_t& index, const Vector2f& uv, Vector3f& N,Vector2f& st) const override{const Vector3f& v0 = vertices[vertexIndex[index * 3]];const Vector3f& v1 = vertices[vertexIndex[index * 3 + 1]];const Vector3f& v2 = vertices[vertexIndex[index * 3 + 2]];Vector3f e0 = normalize(v1 - v0);Vector3f e1 = normalize(v2 - v1);N = normalize(crossProduct(e0, e1));const Vector2f& st0 = stCoordinates[vertexIndex[index * 3]];const Vector2f& st1 = stCoordinates[vertexIndex[index * 3 + 1]];const Vector2f& st2 = stCoordinates[vertexIndex[index * 3 + 2]];st = st0 * (1 - uv.x - uv.y) + st1 * uv.x + st2 * uv.y;}Vector3f evalDiffuseColor(const Vector2f& st) const override{float scale = 5;float pattern = (fmodf(st.x * scale, 1) > 0.5) ^ (fmodf(st.y * scale, 1) > 0.5);return lerp(Vector3f(0.815, 0.235, 0.031), Vector3f(0.937, 0.937, 0.231), pattern);}std::unique_ptr<Vector3f[]> vertices;uint32_t numTriangles;std::unique_ptr<uint32_t[]> vertexIndex;std::unique_ptr<Vector2f[]> stCoordinates;
};

继承自Object类的三角形类Triangle重写了三个虚函数,不过我们的重点在光线与三角形求交的部分:

由之前在笔记六中的算法可以得知这里的做法。

结果:

相关文章:

GAMES101:现代计算机图形学入门-作业五

作业五 这次作业给了许多脚本&#xff0c;我们现在可以把每个脚本的代码逐行细细分析一下。 main.cpp #include "Scene.hpp" #include "Sphere.hpp" #include "Triangle.hpp" #include "Light.hpp" #include "Renderer.hpp&quo…...

GPU 进阶笔记(二):华为昇腾 910B GPU

大家读完觉得有意义记得关注和点赞&#xff01;&#xff01;&#xff01; 1 术语 1.1 与 NVIDIA 术语对应关系1.2 缩写2 产品与机器 2.1 GPU 产品2.2 训练机器 底座 CPU功耗操作系统2.3 性能3 实探&#xff1a;鲲鹏底座 8*910B GPU 主机 3.1 CPU3.2 网卡和网络3.3 GPU 信息 3.3…...

Spring AOP:this 调用当前类方法无法被拦截

问题复现 假设我们正在开发一个宿舍管理系统&#xff0c;这个模块包含一个负责电费充值的类 ElectricService&#xff0c;它含有一个充电方法 charge()&#xff1a; Service public class ElectricService {public void charge() throws Exception {System.out.println("E…...

K8S-LLM:用自然语言轻松操作 Kubernetes

在 Kubernetes (K8s) 的日常管理中&#xff0c;复杂的命令行操作常常让开发者感到头疼。无论是部署应用、管理资源还是调试问题&#xff0c;都需要记住大量的命令和参数。Kubernetes 作为容器编排的行业标准&#xff0c;其强大的功能伴随着陡峭的学习曲线和复杂的命令行操作。这…...

lua和C API库一些记录

相关头文件解释 lua.h&#xff1a;声明lua提供的基础函数&#xff0c;所有内容都有个前缀lua_&#xff1b; luaxlib.h&#xff1a;声明辅助库提供的函数&#xff0c;所有内容都有个前缀luaL_&#xff1b; lualib.h&#xff1a;声明了打开标准库的函数&#xff1b; 辅助库对…...

SpringSecurity中的过滤器链与自定义过滤器

关于 Spring Security 框架中的过滤器的使用方法,系列文章: 《SpringSecurity中的过滤器链与自定义过滤器》 《SpringSecurity使用过滤器实现图形验证码》 1、Spring Security 中的过滤器链 Spring Security 中的过滤器链(Filter Chain)是一个核心的概念,它定义了一系列过…...

Slate文档编辑器-Decorator装饰器渲染调度

Slate文档编辑器-Decorator装饰器渲染调度 在之前我们聊到了基于文档编辑器的数据结构设计&#xff0c;聊了聊基于slate实现的文档编辑器类型系统&#xff0c;那么当前我们来研究一下slate编辑器中的装饰器实现。装饰器在slate中是非常重要的实现&#xff0c;可以为我们方便地…...

本地Docker部署Flowise并实现远程构建LLM应用程序原型高效开发

文章目录 前言1. Docker安装Flowise2. Ubuntu安装Cpolar3. 配置Flowise公网地址4. 远程访问Flowise5. 固定Cpolar公网地址6. 固定地址访问 前言 相信很多对AI感兴趣的小伙伴都会觉得正在逐渐流行的工作流自动化和AI集成特别酷炫&#xff0c;没错&#xff0c;这些技术像“秘密武…...

多点通信、流式域套接字

一、广播 1.1广播的发送端模型&#xff1a; #include<myhead.h>#define BEN_IP "192.168.191.129" #define BEN_PORT 8888#define PORT 6666int main(int argc, const char *argv[]) {int oldfd socket(AF_INET,SOCK_DGRAM,0);if(oldfd -1){perror("soc…...

vue3使用video-player实现视频播放(可拖动视频窗口、调整大小)

1.安装video-player npm install video.js videojs-player/vue --save在main.js中配置全局引入 // 导入视频播放组件 import VueVideoPlayer from videojs-player/vue import video.js/dist/video-js.cssconst app createApp(App) // 视频播放组件 app.use(VueVideoPlayer)2…...

模块化和面向接口的设计:深入理解和应用

模块化和面向接口的设计&#xff1a;深入理解和应用 在面向对象编程中&#xff0c;模块化 和 面向接口设计 是两种非常重要的编程理念。它们能帮助开发人员构建更加清晰、可维护和易于扩展的系统。接下来&#xff0c;我们将详细解释这两种设计思想&#xff0c;并结合 Python 中…...

《SwiftUI 实现点击按钮播放 MP3 音频》

功能介绍 点击按钮时&#xff0c;应用会播放名为 yinpin.mp3 的音频文件。使用 AVAudioPlayer 来加载和播放音频。 关键点&#xff1a; 按钮触发&#xff1a;点击按钮会调用 playAudio() 播放音频。音频加载&#xff1a;通过 Bundle.main.url(forResource:) 加载音频文件。播…...

微机接口课设——基于Proteus和8086的打地鼠设计(8255、8253、8259)Proteus中Unknown 1-byte opcode / Unknown 2-byte opcode错误

原理图设计 汇编代码 ; I/O 端口地址定义 IOY0 EQU 0600H IOY1 EQU 0640H IOY2 EQU 0680HMY8255_A EQU IOY000H*2 ; 8255 A 口端口地址 MY8255_B EQU IOY001H*2 ; 8255 B 口端口地址 MY8255_C EQU IOY002H*2 ; 8255 C 口端口地址 MY8255_MODE EQU IOY003H*2 ; …...

MySQL如何执行.sql 文件:详细教学指南

在使用MySQL数据库过程中&#xff0c;我们经常需要执行包含SQL语句的.sql文件。这些文件通常用于数据库的备份和恢复或批量执行SQL脚本。本文将详细介绍如何在不同环境下执行MySQL的.sql文件。 前置准备 在开始之前&#xff0c;请确保以下条件已经满足&#xff1a; 已经安装…...

非周期性脑活动的动态重构支持癫痫患者的认知功能:一种神经指纹识别方法

摘要 颞叶癫痫(TLE)的特征是大脑活动模式发生大规模的变化&#xff0c;并且这种变化与患者的认知功能受损密切相关。本研究旨在使用神经指纹方法分析大脑活动的动态重构&#xff0c;以描绘TLE患者的个体特征及其认知功能相关性。本研究收集了68名TLE患者和34名对照组的10min静息…...

ZYNQ初识6(zynq_7010)clock时钟IP核

基于板子的PL端无时钟晶振&#xff0c;需要从PS端借用clock1&#xff08;50M&#xff09;晶振 接下去是自定义clock的IP核封装&#xff0c;为后续的simulation可以正常仿真波形&#xff0c;需要注意顶层文件的设置&#xff0c;需要将自定义的IP核对应的.v文件设置为顶层文件&a…...

使用MFC编写一个paddleclas预测软件

目录 写作目的 环境准备 下载编译环境 解压预编译库 准备训练文件 模型文件 图像文件 路径整理 准备预测代码 创建预测应用 新建mfc应用 拷贝文档 配置环境 界面布局 添加回cpp文件 修改函数 报错1解决 报错2未解决 修改infer代码 修改MFCPaddleClasDlg.cp…...

SAP SD BP名称和销售订单描述的对应不起来的问题

问题 VBPA-ADRNR地址 和 KNA1-ADRNR 指向同一个号码 销售订单读取这个地址 改正后恢复正常 原因&#xff1a;推测 应该是创建Y0 电商客户的时候&#xff0c;引起锁和混乱导致的。 具体实际时什么样&#xff0c;不太清楚 写于20241230 浙江台州...

FlastOcc-网络复现-1.环境配置及问题

研究OCC网络 1.RuntimeError: Ninja is required to load C extensions RuntimeError: Ninja is required to load C extensions #32 Ninja is required to load C extensions File “/FlashOCC/projects/mmdet3d_plugin/core/evaluation/ray_metrics.py”, line 12, in dvr …...

Go语言中值接收者和指针接收者的区别?

在 Go 语言中&#xff0c;值接收者和指针接收者是方法定义中的两种接收者类型。它们的主要区别在于方法调用时的行为、接收者是否可以被修改&#xff0c;以及性能上的差异。 值接收者 定义 值接收者的方法接收的是调用对象的一个副本&#xff0c;方法内部对该副本的修改不会影…...

kafka小实站

需要先在前面的文章里面照着下载好kafka&#xff0c;并且启动 先启动zookeeper 项目目录 package kafka; import lombok.extern.slf4j.Slf4j; import org.apache.kafka.clients.consumer.ConsumerRecord; import org.springframework.kafka.annotation.KafkaListener; import…...

基于Python实现车辆检测、机动车检测、识别位置标记、计数

目录 引言背景与应用场景车辆检测的研究意义相关工作车辆检测概述机动车检测方法分类基于传统计算机视觉的检测方法基于深度学习的检测方法技术与方法车辆检测技术概述基于Python的车辆检测方法图像处理与特征提取深度学习方法(如YOLO、SSD、Faster R-CNN等)数据集与标注常用…...

心理学硕士

心理学硕士的主要研究方向包括基础心理学、发展心理学和应用心理学‌。 基础心理学研究一般的心理现象与规律&#xff0c;如心理的实质及神经机制、感觉与知觉、意识与注意、学习与记忆、思维与语言、情绪与意识、人格等‌。发展心理学研究人类个体心理发生发展的特点和规律&a…...

python量化分析学习与实践1:API接口篇

业内比较流行的几款API数据接口&#xff0c;有聚宽、TuShare&#xff0c;yfinance&#xff0c;以及pandas的pandas_datareader等。国内的一般都需要用户认证&#xff0c;才能下载数据。国外的yfinance与pandas_datareader等则不需要&#xff0c;但需要科学上网。 聚宽 测试下…...

【GO基础学习】gin的使用

文章目录 模版使用流程参数传递路由分组数据解析和绑定gin中间件 模版使用流程 package mainimport ("net/http""github.com/gin-gonic/gin" )func main() {// 1.创建路由r : gin.Default()// 2.绑定路由规则&#xff0c;执行的函数// gin.Context&#x…...

网卡状态变更,virtio-net检测

实现方案&#xff1a; 现在在amp模式下linux端有个真实的物理网卡eth0&#xff0c;有一个虚拟网卡virtio-net0后端&#xff0c;此时需要一种机制&#xff0c;将真实物理网卡的状态发送rtos的virtio-net0前端。这里使用register_netdevice_notifier机制&#xff0c;每个virtio-n…...

中华人民共和国保守国家秘密法

中华人民共和国保守国家秘密法 &#xff08;1988年9月5日第七届全国人民代表大会常务委员会第三次会议通过 2010年4月29日第十一届全国人民代表大会常务委员会第十四次会议第一次修订 2024年2月27日第十四届全国人民代表大会常务委员会第八次会议第二次修订&#xff09; 目…...

ELK日志收集系统部署

1、 ElasticSearch部署 Elastic — 搜索 AI 公司 | Elastic 系统类型&#xff1a;Centos7.4 节点IP&#xff1a;172.16.246.234 软件版本&#xff1a;jdk-8u191-linux-x64.tar.gz、elasticsearch-6.5.4.tar.gz 示例节点&#xff1a;172.16.246.234 1、安装配置jdk8 ES运行依…...

3D线上艺术展:艺术与技术的完美融合

随着数字技术的飞速发展&#xff0c;未来的艺术展览正逐步迈向线上线下融合的新阶段。其中&#xff0c;3D线上展览以其独特的魅力&#xff0c;成为线下展览的延伸与拓展&#xff0c;为艺术爱好者们开辟了全新的观赏途径。 对于艺术家和策展人而言&#xff0c;3D线上展览不仅打…...

TiDB 的MPP架构概述

MPP架构介绍&#xff1a; 如图&#xff0c;TiDB Server 作为协调者&#xff0c;首先 TiDB Server 会把每个TiFlash 拥有的region 会在TiFlash上做交换&#xff0c;让表连接在一个TiFlash上。另外 TiFlash会作为计算节点&#xff0c;每个TiFlash都负责数据交换&#xff0c;表连接…...