NeHe OpenGL教程 第三十七课:卡通映射

转自【翻译】NeHe OpenGL 教程

前言

声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改。对NeHe的OpenGL管线教程的编写,以及yarn的翻译整理表示感谢。

 

NeHe OpenGL第三十七课:卡通映射

卡通映射:

什么是卡通了,一个轮廓加上少量的几种颜色。使用一维纹理映射,你也可以实现这种效果。
 
看到人们仍然e-mail我请求在文章中使用我方才在GameDev.net上写的源代码,还看到文章的第二版(在那每一个API附带源码)不是在中途完成之前连贯的结束。我已经把这篇指南一并出租给了NeHe(这实际上是

写文章的最初意图)因此你们所有的OpenGL领袖可以玩转它。对模型的选择表示抱歉,但是我最近一直在玩Quake 2。
注释:这篇文章的源代码可以在这里找到:
http://www.gamedev.net/reference/programming/features/celshading.
这篇指南实际上并不解释原理,仅仅解释代码。在上面的连接中可以发现为什么它能工作。现在不断地大声抱怨STOP E-MAILING ME REQUESTS FOR SOURCE CODE!!!!
首先,我们需要包含一些额外的头文件。第一个(math.h)我们可以使用sqrtf (square root)函数,第二个用来访问文件。

#include <math.h>      
#include <stdio.h>      

现在我们将定义一些结构体来帮助我们存贮我们的数据(保存好几百浮点数组)。第一个是tagMATRIX结构体。如果你仔细地看,你将看到我们正象包含一个十六个浮点数的1维数组~一个2维4×4数族一样存储那个矩阵

。这下至OpenGL存储它的矩阵的方式。如果我们使用4x4数组,这些值将发生错误的顺序。
 
typedef struct tagMATRIX     // 保存OpenGL矩阵的结构体
{
 float Data[16];     // 由于OpenGL的矩阵的格式我们使用[16
}
MATRIX;

第二是向量的类。 仅存储X,Y和Z的值 
  
typedef struct tagVECTOR     // 存储一个单精度向量的结构体
{
 float X, Y, Z;     // 向量的分量
}
VECTOR;

第三,我们持有顶点的结构。每一个顶点仅需要它的法线和位置(没有纹理的现行纵坐标)信息。它们必须以这样的次序被存放,否则当它停止装载文件的事件将发生严重的错误(我发现艰难的情形:(教我分块出租我

的代码。)。
 
typedef struct tagVERTEX     // 存放单一顶点的结构
{
 VECTOR Nor;     // 顶点法线
 VECTOR Pos;     // 顶点位置
}
VERTEX;

最后是多边形的结构。我知道这是存储顶点的愚蠢的方法,要不是它完美工作的简单的缘故。通常我愿意使用一个顶点数组,一个多边形数组,和包括一个在多边形中的3个顶点的指数,但这比较容易显示你想干什么。 
  
typedef struct tagPOLYGON     // 存储单一多边形的结构
{
 VERTEX Verts[3];     // 3个顶点结构数组
}
POLYGON;

优美简单的材料也在这里了。为每一个变量的一个解释考虑那个注释。 
  
bool  outlineDraw = true;    // 绘制轮廓的标记
bool  outlineSmooth = false;    // Anti-Alias 线段的标记
float  outlineColor[3] = { 0.0f, 0.0f, 0.0f };  // 线段的颜色
float  outlineWidth = 3.0f;    // 线段的宽度

VECTOR  lightAngle;     // 灯光的方向
bool  lightRotate = false;    // 是否我们旋转灯光的标记

float  modelAngle = 0.0f;    // 模型的Y轴角度
bool     modelRotate = false;     // 旋转模型的标记

POLYGON  *polyData = NULL;     // 多边形数据
int  polyNum  = 0;    // 多边形的编号

GLuint  shaderTexture[1];     // 存储纹理ID

这是得到的再简单不过的模型文件格式。 最初的少量字节存储在场景中的多边形的编号,文件的其余是tagPOLYGON结构体的一个数组。正因如此,数据在没有任何需要去分类到详细的顺序的情况下被读出。 
  
BOOL ReadMesh ()      // 读“model.txt” 文件
{
 FILE *In = fopen ("Data\\model.txt", "rb");  // 打开文件

 if (!In)
  return FALSE;    // 如果文件没有打开返回 FALSE

 fread (&polyNum, sizeof (int), 1, In);  // 读文件头,多边形的个数

 polyData = new POLYGON [polyNum];   // 分配内存

 fread (&polyData[0], sizeof (POLYGON) * polyNum, 1, In);// 把所有多边形的数据读入

 fclose (In);     // 关闭文件

 return TRUE;     // 工作完成
}

一些基本的数学函数而已。DotProduct计算2个向量或平面之间的角,Magnitude函数计算向量的长度,Normalize函数缩放向量到一个单位长度。
 
inline float DotProduct (VECTOR &V1, VECTOR &V2)  //计算两个向量之间的角度
{
 return V1.X * V2.X + V1.Y * V2.Y + V1.Z * V2.Z;  
}

inline float Magnitude (VECTOR &V)    // 计算向量的长度
{
 return sqrtf (V.X * V.X + V.Y * V.Y + V.Z * V.Z); 
}

void Normalize (VECTOR &V)     // 创建一个单位长度的向量
{
 float M = Magnitude (V);    

 if (M != 0.0f)     // 确保我们没有被0隔开
 {
  V.X /= M;     
  V.Y /= M;
  V.Z /= M;
 }
}

这个函数利用给定的矩阵旋转一个向量。请注意它仅旋转这个向量——与向量的位置相比它算不了什么。它用来当旋转法线确保当我们在计算灯光时它们停留在正确的方向上。 
  
void RotateVector (MATRIX &M, VECTOR &V, VECTOR &D)  // 利用提供的矩阵旋转一个向量
{
 D.X = (M.Data[0] * V.X) + (M.Data[4] * V.Y) + (M.Data[8]  * V.Z); 
 D.Y = (M.Data[1] * V.X) + (M.Data[5] * V.Y) + (M.Data[9]  * V.Z); 
 D.Z = (M.Data[2] * V.X) + (M.Data[6] * V.Y) + (M.Data[10] * V.Z); 
}

引擎的第一个主要的函数…… 初始化,按所说的精确地做。我已经砍掉了在注释中不再需要的代码段。 
  
// 一些GL 初始代码和用户初始化从这里开始
BOOL Initialize (GL_Window* window, Keys* keys)
{

这3个变量用来装载着色文件。在文本文件中为了单一的线段线段包含了空间,虽然shaderData存储了真实的着色值。你可能奇怪为什么我们的96个值被32个代替了。好了,我们需要转换greyscale 值为RGB以便

OpenGL能使用它们。我们仍然可以以greyscale存储这些值,但向上负载纹理时我们至于R,G和B成分仅仅使用同一值。 
  
 char Line[255];      // 255个字符的存储量
 float shaderData[32][3];     // 96个着色值的存储量
 g_window = window; g_keys  = keys;
 FILE *In = NULL;      // 文件指针

当绘制线条时,我们想要确保很平滑。初值被关闭,但是按“2”键,它可以被toggled on/off。 
  
 glShadeModel (GL_SMOOTH);    // 使用色彩阴影平滑
 glDisable (GL_LINE_SMOOTH);    // 线条平滑初始化不可用

 glHint (GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); // 提高计算精度  
 glClearColor (0.7f, 0.7f, 0.7f, 0.0f);  // 设置为灰色背景 
 glClearDepth (1.0f);    // 设置深度缓存值
   glEnable (GL_DEPTH_TEST);  // 启用深度测试
   glDepthFunc (GL_LESS);  // 设置深度比较函数
 glShadeModel (GL_SMOOTH);  // 启用反走样
   glDisable (GL_LINE_SMOOTH);

glEnable (GL_CULL_FACE);    // 启用剔除多边形功能

我们使 OpenGL灯光不可用因为我们自己做所以的灯光计算。 

 glDisable (GL_LIGHTING);    // 使 OpenGL 灯光不可用

这里是我们装载阴影文件的地方。它简单地以32个浮点值ASCII码存放(为了轻松修改),每一个在separate线上。 

 In = fopen ("Data\\shader.txt", "r");   // 打开阴影文件

 if (In)       // 检查文件是否打开
 {
  for (i = 0; i < 32; i++)    // 循环32次
  {
   if (feof (In))    // 检查文件是否结束
    break;

   fgets (Line, 255, In);   // 获得当前线条

这里我们转换 greyscale 值为 RGB, 正象上面所描述的。 

   // 从头到尾复制这个值
   shaderData[i][0] = shaderData[i][1] = shaderData[i][2] = atof (Line);
  }

  fclose (In);     // 关闭文件
 }

 else
  return FALSE;     
  
现在我们向上装载这个纹理。同样它清楚地规定,不要使用任何一种过滤在纹理上否则它看起来奇怪,至少可以这样说。GL_TEXTURE_1D被使用因为它是值的一维数组。 

 glGenTextures (1, &shaderTexture[0]);   // 获得一个自由的纹理ID

 glBindTexture (GL_TEXTURE_1D, shaderTexture[0]);  // 绑定这个纹理。 从现在开始它变为一维

 // 使用邻近点过滤
 glTexParameteri (GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
 glTexParameteri (GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);

 // 设置纹理
 glTexImage1D (GL_TEXTURE_1D, 0, GL_RGB, 32, 0, GL_RGB , GL_FLOAT, shaderData);

现在调整灯光方向。我已经使得它向下指向Z轴正方向,这意味着它将正面碰撞模型 

 lightAngle.X = 0.0f;     
 lightAngle.Y = 0.0f;     
 lightAngle.Z = 1.0f;    

 Normalize (lightAngle);

读取Mesh文件,并返回 
 return ReadMesh ();     // 读取Mesh文件,并返回
}

与上面的函数相对应…… 卸载,删除由Initalize 和 ReadMesh 创建的纹理和多边形数据。 
  
void Deinitialize (void)
{
 glDeleteTextures (1, &shaderTexture[0]);  // 删除阴影纹理

 delete [] polyData;    // 删除多边形数据
}

主要的演示循环。所有这些用来处理输入和更新角度。控制如下:
<SPACE> =锁定旋转

1 = 锁定轮廓绘制
2 = 锁定轮廓 anti-aliasing

<UP> =增加线宽
<DOWN> = 减小线宽
 
void Update (DWORD milliseconds)    // 这里执行动作更新
{
 if (g_keys->keyDown [' '] == TRUE)   // 空格是否被按下
 {
  modelRotate = !modelRotate;   // 锁定模型旋转开/关

  g_keys->keyDown [' '] = FALSE;
 }

 if (g_keys->keyDown ['1'] == TRUE)   // 1是否被按下
 {
  outlineDraw = !outlineDraw;   // 切换是否绘制轮廓线

  g_keys->keyDown ['1'] = FALSE;
 }

 if (g_keys->keyDown ['2'] == TRUE)   // 2是否被按下
 {
  outlineSmooth = !outlineSmooth;  // 切换是否使用反走样

  g_keys->keyDown ['2'] = FALSE;
 }

 if (g_keys->keyDown [VK_UP] == TRUE)   // 上键增加线的宽度
 {
  outlineWidth++;     

  g_keys->keyDown [VK_UP] = FALSE;
 }

 if (g_keys->keyDown [VK_DOWN] == TRUE)  // 下减少线的宽度
 {
  outlineWidth--;     

  g_keys->keyDown [VK_DOWN] = FALSE;
 }

 if (modelRotate)     // 是否旋转
  modelAngle += (float) (milliseconds) / 10.0f; // 更新旋转角度
}

你一直在等待的函数。Draw 函数做每一件事情——计算阴影的值,着色网孔,着色轮廓,等等,这是它作的。 
  
void Draw (void)
{

TmpShade用来存储当前顶点的色度值。所有顶点数据同时被计算,意味着我们只需使用我们能继续使用的单个的变量。
TmpMatrix, TmpVector 和 TmpNormal同样被用来计算顶点数据,TmpMatrix在函数开始时被调整一次并一直保持到Draw函数被再次调用。TmpVector 和 TmpNormal则相反,当另一个顶点被处理时改变。

 float TmpShade;      // 临时色度值

 MATRIX TmpMatrix;      // 临时 MATRIX 结构体
 VECTOR TmpVector, TmpNormal;    // 临时 VECTOR结构体

清除缓冲区矩阵数据 

 glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //  清除缓冲区
 glLoadIdentity ();     // 重置矩阵

首先检查我们是否想拥有平滑的轮廓。如果是,我们就打开anti-alaising 。否则把它关闭。简单! 

 if (outlineSmooth)     // 检查我们是否想要 Anti-Aliased 线条
 {
  glHint (GL_LINE_SMOOTH_HINT, GL_NICEST); // 启用它们
  glEnable (GL_LINE_SMOOTH);   
 }

 else      // 否则不启用
  glDisable (GL_LINE_SMOOTH);  
  
然后我们设置视口。我们反向移动摄象机2个单元,之后以一定角度旋转模型。注:由于我们首先移动摄象机,这个模型将在现场旋转。如果我们以另一种方法做,模型将绕摄象机旋转。
我们之后从OpenGL中取最新创建的矩阵并把它存储在 TmpMatrix。

 glTranslatef (0.0f, 0.0f, -2.0f);   // 移入屏幕两个单位
 glRotatef (modelAngle, 0.0f, 1.0f, 0.0f);  // 绕Y轴旋转这个模型

 glGetFloatv (GL_MODELVIEW_MATRIX, TmpMatrix.Data); // 获得产生的矩阵

戏法开始了。首先我们启用一维纹理,然后启用着色纹理。这被OpenGL用来当作一个look-up表格。我们之后调整模型的颜色(白色)我选择白色是因为它亮度高并且描影法比其它颜色好。我建议你不要使用黑色:)

 // 卡通渲染代码
 glEnable (GL_TEXTURE_1D);    // 启用一维纹理
 glBindTexture (GL_TEXTURE_1D, shaderTexture[0]); // 锁定我们的纹理

 glColor3f (1.0f, 1.0f, 1.0f);    // 调整模型的颜色

现在我们开始绘制那些三角形。尽管我们看到在数组中的每一个多边形,然后旋转它的每一个顶点。第一步是拷贝法线信息到一个临时的结构中。因此我们能旋转法线,但仍然保留原来保存的值(没有精确降级)。

 glBegin (GL_TRIANGLES);     // 告诉 OpenGL 我们即将绘制三角形

  for (i = 0; i < polyNum; i++)   // 从头到尾循环每一个多边形
  {
   for (j = 0; j < 3; j++)   // 从头到尾循环每一个顶点
   {
    TmpNormal.X = polyData[i].Verts[j].Nor.X; // 用当前顶点的法线值填充TmpNormal结构
    TmpNormal.Y = polyData[i].Verts[j].Nor.Y; 
    TmpNormal.Z = polyData[i].Verts[j].Nor.Z;

第二,我们通过初期从OpenGL中攫取的矩阵来旋转这个法线。我们之后规格化因此它并不全部变为螺旋形。 

    // 通过矩阵旋转
    RotateVector (TmpMatrix, TmpNormal, TmpVector);

    Normalize (TmpVector);  // 规格化这个新法线

第三,我们获得那个旋转的法线的点积灯光方向(称为lightAngle,因为我忘了从我的旧的light类中改变它)。我们之后约束这个值在0——1的范围。(从-1到+1)

    // 计算色度值
    TmpShade = DotProduct (TmpVector, lightAngle);

    if (TmpShade < 0.0f)
     TmpShade = 0.0f; // 如果负值约束这个值到0

第四,对于OpenGL我们象忽略纹理坐标一样忽略这个值。阴影纹理与一个查找表一样来表现(色度值正成为指数),这是(我认为)为什么1D纹理被创造主要原因。对于OpenGL我们之后忽略这个顶点位置,并不断重

复,重复。至此我认为你已经抓到了概念。 

    glTexCoord1f (TmpShade); // 规定纹理的纵坐标当作这个色度值
    // 送顶点
    glVertex3fv (&polyData[i].Verts[j].Pos.X);
      }
  }

 glEnd ();      // 告诉OpenGL 完成绘制

 glDisable (GL_TEXTURE_1D);    // 1D 纹理不可用

现在我们转移到轮廓之上。一个轮廓能以“它的相邻的边,一边为可见,另一边为不可见”定义。在OpenGL中,这是深度测试被规定小于或等于(GL_LEQUAL)当前值的地方,并且就在那时所有前面的面被精选。我们同

样也要混合线条,以使它看起来不错:)
那么,我们使混合可用并规定混合模式。我们告诉OpenGL与着色线条一样着色backfacing多边形,并且规定这些线条的宽度。我们精选所有前面多边形,并规定测试深度小于或等于当前的Z值。在这个线条的的颜色被

规定后,我们从头到尾循环每一个多边形,绘制它的顶点。我们仅需忽略顶点位置,而不是法线或着色值因为我们需要的仅仅是轮廓。

 // 轮廓代码
 if (outlineDraw)      // 检查看是否我们需要绘制轮廓
 {
  glEnable (GL_BLEND);    // 使混合可用
  // 调整混合模式
  glBlendFunc (GL_SRC_ALPHA ,GL_ONE_MINUS_SRC_ALPHA);

  glPolygonMode (GL_BACK, GL_LINE);   // 绘制轮廓线
  glLineWidth (outlineWidth);    // 调整线宽

  glCullFace (GL_FRONT);    // 剔出前面的多边形

  glDepthFunc (GL_LEQUAL);    // 改变深度模式

  glColor3fv (&outlineColor[0]);   // 规定轮廓颜色

  glBegin (GL_TRIANGLES);    // 告诉OpenGL我们想要绘制什么

   for (i = 0; i < polyNum; i++)  // 从头到尾循环每一个多边形
   {
    for (j = 0; j < 3; j++)  // 从头到尾循环每一个顶点
    {
     // 送顶点
     glVertex3fv (&polyData[i].Verts[j].Pos.X);
    }
   }

  glEnd ();      // 告诉 OpenGL我们已经完成

这样以后,我们就把它规定为以前的状态,然后退出 

  glDepthFunc (GL_LESS);    // 重置深度测试模式

  glCullFace (GL_BACK);    // 重置剔出背面多边形

  glPolygonMode (GL_BACK, GL_FILL);   // 重置背面多边形绘制方式

  glDisable (GL_BLEND);    //  混合不可用
 }
}
原文及其个版本源代码下载:

http://nehe.gamedev.net/data/lessons/lesson.asp?lesson=37

转载于:https://www.cnblogs.com/arxive/p/6239540.html

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

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

相关文章

SDN交换机在云计算网络中的应用场景

SDN的技术已经发展了好几年了&#xff0c;而云计算的历史更长&#xff0c;两者的结合更是作为SDN的一个杀手级应用在近两年炒得火热&#xff0c;一些知名咨询公司的关于SDN逐年增加的市场份额的论断&#xff0c;也主要是指SDN在云计算网络中的应用。 关于SDN在云计算网络中的应…

sql server 里面怎么支持数字使用双引号_国查:用中文编写SQL

这两天被 文言(wenyan-lang)刷屏了&#xff0c;这个项目在于使用文言文进行编程&#xff0c;我打算蹭个热度&#xff0c;把年初的作品再捞一捞&#xff0c;即中文SQL。1. 文言Wenyan&#xff1a;吾有一數。曰三。名之曰「甲」。為是「甲」遍。吾有一言。曰「「問天地好在。」」…

七日掌握设计配色基础_掌握正确的基础知识:如何设计网站的导航,搜索和首页...

七日掌握设计配色基础by Anant Jain通过Anant Jain 掌握正确的基础知识&#xff1a;如何设计网站的导航&#xff0c;搜索和首页 (Get the basics right: how to design your site’s navigation, search, and homepage) 一个7分钟的指南&#xff0c;使这三个基础组件正确无误。…

python渲染光线_python模板渲染配置文件

python的mako、jinja2模板库&#xff0c;确实好用&#xff01;这里做个笔记&#xff0c;好记性不如烂笔头。#!/usr/bin/env python#encodingutf-8import sys,yaml # 配置文件使用yaml格式from mako.template import Template # 加载mako库的Templat…

leetcode114. 二叉树展开为链表(深度优先搜索)

给定一个二叉树&#xff0c;原地将它展开为一个单链表。例如&#xff0c;给定二叉树1/ \2 5/ \ \ 3 4 6 将其展开为&#xff1a;1\2\3\4\5\6代码 class Solution {public void flatten(TreeNode root) {flat(root);}public TreeNode flat(TreeNode root) {if(rootnull)…

eclipse新建web项目

需要点击File—>New—>Other…在Web文件夹下找到Dynamic Web Project—>Next修改server端口可以在启动项目后访问地址是端口号项目名转载于:https://juejin.im/post/5cb4999df265da037b610545

idea tips

AltInsert 自动出现generate ,,里面有构造方法&#xff0c;getter,setter... CtrlO,重写方法 CtrlI...自动出现接口的方法 转载于:https://www.cnblogs.com/bin-lin/p/6247538.html

革新以太网交换机架构 全光网络的风刮进园区

全光网络的风正在刮进园区网&#xff0c;众所周知&#xff0c;光纤入户发展迅速&#xff0c;随着PON&#xff08;无源光纤网络&#xff09;技术在运营商通信网络的大规模使用&#xff0c;PON相关产业链逐步成熟&#xff0c;这也使得PON技术逐步在企业园区网得到应用。 基于铜线…

mysql loop循环实例_MySql CURSOR+LOOP循环-使用小实例

转载自https://blog.csdn.net/starinbrook/article/details/77078126转载自https://blog.csdn.net/makang456/article/details/53896346/【简介】游标实际上是一种能从包括多条数据记录的结果集中每次提取一条记录的机制。游标充当指针的作用。尽管游标能遍历结果中的所有行&am…

react数据从本地读取_如何将从Google表格读取的React应用程序部署到Netlify

react数据从本地读取In this tutorial, we’re going to cover how to connect to a spreadsheet hosted on Google, display that information inside a React application, and deploy it to Netlify.在本教程中&#xff0c;我们将介绍如何连接到Google托管的电子表格&#x…

leetcode743. 网络延迟时间(迪杰斯特拉算法)

有 N 个网络节点&#xff0c;标记为 1 到 N。 给定一个列表 times&#xff0c;表示信号经过有向边的传递时间。 times[i] (u, v, w)&#xff0c;其中 u 是源节点&#xff0c;v 是目标节点&#xff0c; w 是一个信号从源节点传递到目标节点的时间。 现在&#xff0c;我们从某个…

在线python视频教程_【好程序员】2019 Python全套视频教程2

2019千锋好程序员全新Python教程&#xff0c;深入浅出的讲解Python语言的基础语法&#xff0c;注重基本编程能力训练&#xff0c;深入解析面向对象思想&#xff0c;数据类型和变量、运算符、流程控制、函数、面向对象、模块和包、生成器和迭代器。教程列表&#xff1a;千锋Pyth…

洛谷——P1546 最短网络 Agri-Net

P1546 最短网络 Agri-Net 题目背景 农民约翰被选为他们镇的镇长&#xff01;他其中一个竞选承诺就是在镇上建立起互联网&#xff0c;并连接到所有的农场。当然&#xff0c;他需要你的帮助。 题目描述 约翰已经给他的农场安排了一条高速的网络线路&#xff0c;他想把这条线路共享…

漫谈单点登录(SSO)(淘宝天猫)(转载)

1. 摘要 &#xff08; 注意&#xff1a;请仔细看下摘要&#xff0c;留心此文是否是您的菜&#xff0c;若浪费宝贵时间&#xff0c;深感歉意&#xff01;&#xff01;&#xff01;&#xff09; SSO这一概念由来已久&#xff0c;网络上对应不同场景的成熟SSO解决方案比比皆是&…

mysql mdl 锁_MySQL MDL锁

MDL全称为metadata lock&#xff0c;即元数据锁。MDL锁主要作用是维护表元数据的数据一致性&#xff0c;在表上有活动事务(显式或隐式)的时候&#xff0c;不可以对元数据进行写入操作。因此从MySQL5.5版本开始引入了MDL锁&#xff0c;来保护表的元数据信息&#xff0c;用于解决…

Card Game Again CodeForces - 818E (双指针)

大意: 给定序列, 求多少个区间积被k整除. 整除信息满足单调性, 显然双指针. 具体实现只需要考虑k的素数向量, 对每一维维护个指针即可. 这题看了下cf其他人的做法, 发现可以直接暴力, 若当前的前缀积模k为0, 暴力向前求出第一个后缀积为0的位置即可, 复杂度是$O(n)$的并且相当好…

pacf和acf_如何通过Wordpress API,ACF和Express.js使Wordpress更加令人兴奋

pacf和acfby Tyler Jackson泰勒杰克逊(Tyler Jackson) 如何通过Wordpress API&#xff0c;ACF和Express.js使Wordpress更加令人兴奋 (How to make Wordpress more exciting with the Wordpress API, ACF, & Express.js) I’ve been working with Wordpress since it’s pr…

python运行出现数据错误_Python运行出错情况

1、错误内容&#xff1a;You must not use 8-bit bytestrings unless you use a text_factory that can interpret 8-bit bytestrings (like text_factory str). It is highly recommended that you instead just switch your application to Unicode strings.错误描述&#x…

leetcode95. 不同的二叉搜索树 II(递归)

给定一个整数 n&#xff0c;生成所有由 1 ... n 为节点所组成的 二叉搜索树 。示例&#xff1a;输入&#xff1a;3 输出&#xff1a; [[1,null,3,2],[3,2,null,1],[3,1,null,null,2],[2,1,3],[1,null,2,null,3] ] 解释&#xff1a; 以上的输出对应以下 5 种不同结构的二叉搜索树…

数据结构探险系列—栈篇-学习笔记

数据结构探险—栈篇 什么是栈&#xff1f; 古代栈就是牲口棚的意思。 栈是一种机制&#xff1a;后进先出 LIFO&#xff08;last in first out&#xff09; 电梯 栈要素空栈。栈底&#xff0c;栈顶。没有元素的时候&#xff0c;栈顶和栈底指向同一个元素&#xff0c;如果加入新元…