DirectX11 With Windows SDK--15 几何着色器初探
前言
从这一部分开始,感觉就像是踏入了无人深空一样,在之前初学DX11的时候,这部分内容都是基本上跳过的,现在打算重新认真地把它给拾回来。
DirectX11 With Windows SDK完整目录
Github项目源码
欢迎加入QQ群: 727623616 可以一起探讨DX11,以及有什么问题也可以在这里汇报。
几何着色器
首先用一张图来回顾一下渲染管线的各个阶段,目前为止我们接触的着色器有顶点着色器和像素着色器,而接触到的渲染管线阶段有:输入装配阶段、顶点着色阶段、光栅化阶段、像素着色阶段、输出合并阶段。
可以看到,几何着色器是我们在将顶点送入光栅化阶段之前,可以操作顶点的最后一个阶段。它同样也允许我们编写自己的着色器代码。几何着色器可以做如下事情:
- 让程序自动决定如何在渲染管线中插入/移除几何体;
- 通过流输出阶段将顶点信息再次传递到顶点缓冲区;
- 改变图元类型(如输入点图元,输出三角形图元);
但它也有缺点,几何着色器输出的顶点数据很可能是有较多重复的,从流输出拿回到顶点缓冲区的话会占用较多的内存空间。它本身无法输出索引数组。
几何着色阶段会收到一系列代表输入几何体类型的顶点,然后我们可以自由选择其中的这些顶点信息,然后交给流输出对象重新解释成新的图元类型(或者不变),传递给流输出阶段或者是光栅化阶段。而几何着色器仅能够接受来自输入装配阶段提供的顶点信息,对每个顶点进行处理,无法自行决定增减顶点。
注意:离开几何着色器的顶点如果要传递给光栅化阶段,需要包含有转换到齐次裁剪坐标系的坐标信息(语义为SV_POSITION
的float4
向量)
可编程的几何着色器
从一个看似没什么用的几何着色器代码入手
若我们直接从VS项目新建一个几何着色器文件,则可以看到下面的代码:
struct GSOutput
{float4 pos : SV_POSITION;
};[maxvertexcount(3)]
void main(triangle float4 input[3] : SV_POSITION, inout TriangleStream< GSOutput > output
)
{for (uint i = 0; i < 3; i++){GSOutput element;element.pos = input[i];output.Append(element);}
}
ps. 可能有些人会对void main的写法表示不爽,比如说我。不过这不是C语言的主函数......
若在输入装配阶段指定使用TriangleList图元的话,初步观察该代码,实际上你可以发现其实该着色器只是把输入的顶点按原样输出给流输出对象,即跟什么都没做(咸鱼)有什么区别。。不过从这份代码里面就已经包含了几何着色器所特有的绝大部分语法了。
首先,几何着色器是根据图元类型来进行调用的,若使用的是TriangleList,则每一个三角形的三个顶点都会作为输入,触发几何着色器的调用。这样一个TriangleList解释的30个顶点会触发10次调用。
对于几何着色器,我们必须要指定它每次调用所允许输出的最大顶点数目。我们可以使用属性语法来强行修改着色器行为:
[maxvertexcount(N)]
这里N
就是每次调用允许产出的最大顶点数目,然后最终输出的顶点数目不会超过N
的值。maxvertexcount
的值应当尽可能的小。
关于性能上的表现,我根据龙书提供的引用找到了对应的说明文档:
NVIDIA08
虽然是10年前的文档,这里说到:在GeForce 8800 GTX,一个几何着色器的调用在输出1到20个标量的时候可以达到最大运行性能表现,但是当我们指定最大允许输出标量的数目在27到40个时,性能仅达到峰值的50%。比如说,如果顶点的声明如下:
struct V0
{float3 pos : POSITION;float2 tex : TEXCOORD;
};
这里每个顶点就已经包含了5个标量了,如果以它作为输出类型,则maxvertexcount
为4的时候就可以达到理论上的峰值性能(20个标量)。
但如果顶点类型中还包含有float3
类型的法向量,每个顶点就额外包含了3个标量,这样在maxvertexcount
为4的时候就输出了32个标量,只有50%的峰值性能表现。
这份文档已经将近10年了,对于那时候的显卡来说使用几何着色器可能不是一个很好的选择,不过当初的显卡也早已不能和现在的显卡相提并论了。
注意:
maxvertexcount
的值应当设置到尽可能小的值,因为它将直接决定几何着色器的运行效率。- 几何着色器的每次调用最多只能处理1024个标量,对于只包含4D位置向量的顶点来说也只能处理256个顶点。
- 几何着色器输入的结构体类型不允许超过128个标量,对于只包含4D位置向量的顶点来说也只能包含32个顶点。
在HLSL编译器里,如果设置的maxvertexcount
过大,会直接收到编译错误:
然后代码中的triangle
是用于指定输入的图元类型,具体支持的关键字如下:
图元类型 | 描述 |
---|---|
point | Point list |
line | Line list or line strip |
triangle | Triangle list or triangle strip |
lineadj | Line list with adjacency or line strip with adjacency |
triangleadj | Triangle list with adjacency or triangle strip with adjacency |
具体的图元类型可以到第2章回顾:点击此处
而参数类型可以是用户自定义的结构体类型,或者是向量(float4)类型。从顶点着色器传过来的顶点至少会包含一个表示齐次裁剪坐标的向量。
参数名inupt
实际上用户是可以任意指定的。
对于该输入参数的元素数目,取决于前面声明的图元类型:
图元类型 | 元素数目 |
---|---|
point | [1] 每次只能处理1个顶点 |
line | [2] 一个线段必须包含2个顶点 |
triangle | [3] 一个三角形需要3个顶点 |
lineadj | [4] 一个邻接线段需要4个顶点 |
triangleadj | [6] 一个邻接三角形需要6个顶点 |
而第二个参数必须是一个流输出对象,而且需要被指定为inout
可读写类型。可以看到,它是一个类模板,模板的形参指定要输出的类型。流输出对象有如下三种:
流输出对象类型 | 描述 |
---|---|
PointStream | 一系列点的图元 |
LineStream | 一系列线段的图元 |
TriangleStream | 一系列三角形的图元 |
流输出对象都具有下面两种方法:
方法 | 描述 |
---|---|
Append | 向指定的流输出对象添加一个输出的数据 |
RestartStrip |
在以线段或者三角形作为图元的时候,默认是以strip的形式输出的, 如果我们不希望下一个输出的顶点与之前的顶点构成新图元,则需要调用此方法来重新开始新的strip。若希望输出的图元类型也保持和原 来一样的TriangleList,则需要每调用3次Append方法后就调用一次RestartStrip。 |
注意:
- 所谓的删除顶点,实际上就是不将该顶点传递给流输出对象
- 若传入的顶点中多余的部分无法构成对应的图元,则抛弃掉这些多余的顶点
在开始前,先放出Basic.hlsli
文件的内容:
#include "LightHelper.hlsli"cbuffer CBChangesEveryFrame : register(b0)
{matrix g_World;matrix g_WorldInvTranspose;
}cbuffer CBChangesOnResize : register(b1)
{matrix g_Proj;
}cbuffer CBChangesRarely : register(b2)
{DirectionalLight g_DirLight[5];PointLight g_PointLight[5];SpotLight g_SpotLight[5];Material g_Material;matrix g_View;float3 g_EyePosW;float g_CylinderHeight;
}struct VertexPosColor
{float3 PosL : POSITION;float4 Color : COLOR;
};struct VertexPosHColor
{float4 PosH : SV_POSITION;float4 Color : COLOR;
};
实战1: 将一个三角形分割成三个三角形
现在我们的目标是把一个三角形分裂成三个三角形:
这也为以后实现分形做为基础。建议读者可以先自行尝试编写着色器代码再来对比。在编写好着色器代码后,
要给渲染管线绑定好一切所需的资源才能够看到效果。
HLSL代码
Triangle_VS.hlsl
, Triangle_GS.hlsl
和Triangle_PS.hlsl
的实现如下:
// Triangle_VS.hlsl
#include "Basic.hlsli"VertexPosHColor VS(VertexPosColor vIn)
{matrix worldViewProj = mul(mul(g_World, g_View), g_Proj);VertexPosHColor vOut;vOut.Color = vIn.Color;vOut.PosH = mul(float4(vIn.PosL, 1.0f), worldViewProj);return vOut;
}
// Triangle_GS.hlsl
#include "Basic.hlsli"[maxvertexcount(9)]
void GS(triangle VertexPosHColor input[3], inout TriangleStream<VertexPosHColor> output)
{//// 将一个三角形分裂成三个三角形,即没有v3v4v5的三角形// v1// /\// / \// v3/____\v4// /\xxxx/\// / \xx/ \// /____\/____\// v0 v5 v2VertexPosHColor vertexes[6];int i;[unroll]for (i = 0; i < 3; ++i){vertexes[i] = input[i];vertexes[i + 3].Color = (input[i].Color + input[(i + 1) % 3].Color) / 2.0f;vertexes[i + 3].PosH = (input[i].PosH + input[(i + 1) % 3].PosH) / 2.0f;}[unroll]for (i = 0; i < 3; ++i){output.Append(vertexes[i]);output.Append(vertexes[3 + i]);output.Append(vertexes[(i + 2) % 3 + 3]);output.RestartStrip();}
}
// Triangle_PS.hlsl
#include "Basic.hlsli"float4 PS(VertexPosHColor pIn) : SV_Target
{return pIn.Color;
}
这里输入和输出的图元类型都是一致的,但无论什么情况都一定要注意设置好maxvertexcount
的值,这里固定一个三角形的三个顶点输出9个顶点(构成三个三角形),并且每3次Append
就需要调用1次RestartStrip
。
实战2: 通过圆线构造圆柱体侧面
已知图元类型为LineStrip,现在有一系列连续的顶点构成圆线(近似圆弧的连续折线),构造出圆柱体的侧面。即输入图元类型为线段,输出一个矩形(两个三角形)。
思路: 光有顶点位置还不足以构造出圆柱体侧面,因为无法确定圆柱往哪个方向延伸。所以我们还需要对每个顶点引入所在圆柱侧面的法向量,通过叉乘就可以确定上方向/下方向并进行延伸了。
HLSL代码
Cylinder_VS.hlsl
, Cylinder_GS.hlsl
和Cylinder_PS.hlsl
的实现如下:
// Cylinder_VS.hlsl
#include "Basic.hlsli"VertexPosHWNormalColor VS(VertexPosNormalColor vIn)
{VertexPosHWNormalColor vOut;matrix viewProj = mul(g_View, g_Proj);float4 posW = mul(float4(vIn.PosL, 1.0f), g_World);vOut.PosH = mul(posW, viewProj);vOut.PosW = posW.xyz;vOut.NormalW = mul(vIn.NormalL, (float3x3) g_WorldInvTranspose);vOut.Color = vIn.Color;return vOut;
}
// Cylinder_GS.hlsl
#include "Basic.hlsli"// 一个v0v1线段输出6个三角形顶点
[maxvertexcount(6)]
void GS(line VertexPosHWNormalColor input[2], inout TriangleStream<VertexPosHWNormalColor> output)
{// *****************************// 要求圆线是顺时针的,然后自底向上构造圆柱侧面 // --> v2____v3// ______ |\ |// / \ | \ |// \______/ | \ |// <-- |___\|// v1(i1) v0(i0)float3 upDir = normalize(cross(input[0].NormalW, (input[1].PosW - input[0].PosW)));VertexPosHWNormalColor v2, v3;matrix viewProj = mul(g_View, g_Proj);v2.PosW = input[1].PosW + upDir * g_CylinderHeight;v2.PosH = mul(float4(v2.PosW, 1.0f), viewProj);v2.NormalW = input[1].NormalW;v2.Color = input[1].Color;v3.PosW = input[0].PosW + upDir * g_CylinderHeight;v3.PosH = mul(float4(v3.PosW, 1.0f), viewProj);v3.NormalW = input[0].NormalW;v3.Color = input[0].Color;output.Append(input[0]);output.Append(input[1]);output.Append(v2);output.RestartStrip();output.Append(v2);output.Append(v3);output.Append(input[0]);
}
// Cylinder_PS.hlsl
#include "Basic.hlsli"float4 PS(VertexPosHWNormalColor pIn) : SV_Target
{// 标准化法向量pIn.NormalW = normalize(pIn.NormalW);// 顶点指向眼睛的向量float3 toEyeW = normalize(g_EyePosW - pIn.PosW);// 初始化为0 float4 ambient = float4(0.0f, 0.0f, 0.0f, 0.0f);float4 diffuse = float4(0.0f, 0.0f, 0.0f, 0.0f);float4 spec = float4(0.0f, 0.0f, 0.0f, 0.0f);// 只计算方向光ComputeDirectionalLight(g_Material, g_DirLight[0], pIn.NormalW, toEyeW, ambient, diffuse, spec);return pIn.Color * (ambient + diffuse) + spec;
}
实战3: 画出顶点的法向量
画出顶点的法向量可以帮助你进行调试,排查法向量是否出现了问题。这时候图元的类型为PointList,需要通过几何着色器输出一个线段(两个顶点)。由于顶点中包含法向量,剩下的就是要自行决定法向量的长度。
下图的法向量长度为0.5
HLSL代码
Normal_VS.hlsl
, Normal_GS.hlsl
和Normal_PS.hlsl
的实现如下:
// Normal_VS.hlsl
#include "Basic.hlsli"VertexPosHWNormalColor VS(VertexPosNormalColor vIn)
{VertexPosHWNormalColor vOut;matrix viewProj = mul(g_View, g_Proj);float4 posW = mul(float4(vIn.PosL, 1.0f), g_World);vOut.PosH = mul(posW, viewProj);vOut.PosW = posW.xyz;vOut.NormalW = mul(vIn.NormalL, (float3x3) g_WorldInvTranspose);vOut.Color = vIn.Color;return vOut;
}
// Normal_GS.hlsl
#include "Basic.hlsli"[maxvertexcount(2)]
void GS(point VertexPosHWNormalColor input[1], inout LineStream<VertexPosHWNormalColor> output)
{matrix viewProj = mul(g_View, g_Proj);VertexPosHWNormalColor v;// 防止资源争夺v.PosW = input[0].PosW + input[0].NormalW * 0.01f;v.NormalW = input[0].NormalW;v.PosH = mul(float4(v.PosW, 1.0f), viewProj);v.Color = input[0].Color;output.Append(v);v.PosW = v.PosW + input[0].NormalW * 0.5f;v.PosH = mul(float4(v.PosW, 1.0f), viewProj);output.Append(v);
}
// Normal_PS.hlsl
#include "Basic.hlsli"float4 PS(VertexPosHWNormalColor pIn) : SV_TARGET
{return pIn.Color;
}
C++代码的部分变化
BasicEffect的变化
变化如下:
class BasicEffect : public IEffect
{
public:BasicEffect();virtual ~BasicEffect() override;BasicEffect(BasicEffect&& moveFrom) noexcept;BasicEffect& operator=(BasicEffect&& moveFrom) noexcept;// 获取单例static BasicEffect& Get();// 初始化Basic.hlsli所需资源并初始化渲染状态bool InitAll(ID3D11Device * device);//// 渲染模式的变更//// 绘制三角形分裂void SetRenderSplitedTriangle(ID3D11DeviceContext * deviceContext);// 绘制无上下盖的圆柱体void SetRenderCylinderNoCap(ID3D11DeviceContext * deviceContext);// 绘制所有顶点的法向量void SetRenderNormal(ID3D11DeviceContext * deviceContext);//// 矩阵设置//void XM_CALLCONV SetWorldMatrix(DirectX::FXMMATRIX W);void XM_CALLCONV SetViewMatrix(DirectX::FXMMATRIX V);void XM_CALLCONV SetProjMatrix(DirectX::FXMMATRIX P);//// 光照、材质和纹理相关设置//// 各种类型灯光允许的最大数目static const int maxLights = 5;void SetDirLight(size_t pos, const DirectionalLight& dirLight);void SetPointLight(size_t pos, const PointLight& pointLight);void SetSpotLight(size_t pos, const SpotLight& spotLight);void SetMaterial(const Material& material);void XM_CALLCONV SetEyePos(DirectX::FXMVECTOR eyePos);// 设置圆柱体侧面高度void SetCylinderHeight(float height);// 应用常量缓冲区和纹理资源的变更void Apply(ID3D11DeviceContext * deviceContext);private:class Impl;std::unique_ptr<Impl> pImpl;
};
BasicEffect::SetRenderSplitedTriangle方法--渲染分裂的三角形
该方法处理的是图元TriangleList。因为后续的方法处理的图元不同,在调用开始就得设置回正确的图元。也请确保输入装配阶段提供好需要分裂的三角形顶点。
void BasicEffect::SetRenderSplitedTriangle(ID3D11DeviceContext * deviceContext)
{deviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);deviceContext->IASetInputLayout(pImpl->m_pVertexPosColorLayout.Get());deviceContext->VSSetShader(pImpl->m_pTriangleVS.Get(), nullptr, 0);deviceContext->GSSetShader(pImpl->m_pTriangleGS.Get(), nullptr, 0);deviceContext->RSSetState(nullptr);deviceContext->PSSetShader(pImpl->m_pTrianglePS.Get(), nullptr, 0);}
BasicEffect::SetRenderCylinderNoCap方法--渲染圆柱侧面
该方法处理的是图元LineStrip,确保输入的一系列顶点和法向量能够在同一平面上。若提供的顶点集合按顺时针排布,则会自底向上构建出圆柱体,反之则是自顶向下构建。
这里需要关闭背面裁剪,因为我们也可以看到圆柱侧面的内部(没有盖子)。
void BasicEffect::SetRenderCylinderNoCap(ID3D11DeviceContext * deviceContext)
{deviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_LINESTRIP);deviceContext->IASetInputLayout(pImpl->m_pVertexPosNormalColorLayout.Get());deviceContext->VSSetShader(pImpl->m_pCylinderVS.Get(), nullptr, 0);deviceContext->GSSetShader(pImpl->m_pCylinderGS.Get(), nullptr, 0);deviceContext->RSSetState(RenderStates::RSNoCull.Get());deviceContext->PSSetShader(pImpl->m_pCylinderPS.Get(), nullptr, 0);}
BasicEffect::SetRenderNormal方法--渲染法向量
该方法处理的图元是PointList,确保输入的顶点要包含法向量。
void BasicEffect::SetRenderNormal(ID3D11DeviceContext * deviceContext)
{deviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_POINTLIST);deviceContext->IASetInputLayout(pImpl->m_pVertexPosNormalColorLayout.Get());deviceContext->VSSetShader(pImpl->m_pNormalVS.Get(), nullptr, 0);deviceContext->GSSetShader(pImpl->m_pNormalGS.Get(), nullptr, 0);deviceContext->RSSetState(nullptr);deviceContext->PSSetShader(pImpl->m_pNormalPS.Get(), nullptr, 0);}
GameApp类的变化
该项目包含上面三种实战内容,需要用户去指定当前播放的模式。
首先声明部分变化如下:
class GameApp : public D3DApp
{
public:enum class Mode { SplitedTriangle, CylinderNoCap, CylinderNoCapWithNormal };public:GameApp(HINSTANCE hInstance);~GameApp();bool Init();void OnResize();void UpdateScene(float dt);void DrawScene();private:bool InitResource();void ResetTriangle();void ResetRoundWire();private:ComPtr<ID2D1SolidColorBrush> m_pColorBrush; // 单色笔刷ComPtr<IDWriteFont> m_pFont; // 字体ComPtr<IDWriteTextFormat> m_pTextFormat; // 文本格式ComPtr<ID3D11Buffer> m_pVertexBuffer; // 顶点集合int m_VertexCount; // 顶点数目Mode m_ShowMode; // 当前显示模式BasicEffect m_BasicEffect; // 对象渲染特效管理};
GameApp::ResetTriangle方法--重设为三角形顶点
void GameApp::ResetTriangle()
{// ******************// 初始化三角形//// 设置三角形顶点VertexPosColor vertices[] ={{ XMFLOAT3(-1.0f * 3, -0.866f * 3, 0.0f), XMFLOAT4(1.0f, 0.0f, 0.0f, 1.0f) },{ XMFLOAT3(0.0f * 3, 0.866f * 3, 0.0f), XMFLOAT4(0.0f, 1.0f, 0.0f, 1.0f) },{ XMFLOAT3(1.0f * 3, -0.866f * 3, 0.0f), XMFLOAT4(0.0f, 0.0f, 1.0f, 1.0f) }};// 设置顶点缓冲区描述D3D11_BUFFER_DESC vbd;ZeroMemory(&vbd, sizeof(vbd));vbd.Usage = D3D11_USAGE_IMMUTABLE;vbd.ByteWidth = sizeof vertices;vbd.BindFlags = D3D11_BIND_VERTEX_BUFFER;vbd.CPUAccessFlags = 0;// 新建顶点缓冲区D3D11_SUBRESOURCE_DATA InitData;ZeroMemory(&InitData, sizeof(InitData));InitData.pSysMem = vertices;HR(m_pd3dDevice->CreateBuffer(&vbd, &InitData, m_pVertexBuffer.ReleaseAndGetAddressOf()));// 三角形顶点数m_VertexCount = 3;
}
GameApp::ResetRoundWire方法--重设为圆线顶点
void GameApp::ResetRoundWire()
{// ****************** // 初始化圆线// 设置圆边上各顶点// 必须要按顺时针设置// 由于要形成闭环,起始点需要使用2次// ______// / \// \______///VertexPosNormalColor vertices[41];for (int i = 0; i < 40; ++i){vertices[i].pos = XMFLOAT3(cosf(XM_PI / 20 * i), -1.0f, -sinf(XM_PI / 20 * i));vertices[i].normal = XMFLOAT3(cosf(XM_PI / 20 * i), 0.0f, -sinf(XM_PI / 20 * i));vertices[i].color = XMFLOAT4(1.0f, 1.0f, 1.0f, 1.0f);}vertices[40] = vertices[0];// 设置顶点缓冲区描述D3D11_BUFFER_DESC vbd;ZeroMemory(&vbd, sizeof(vbd));vbd.Usage = D3D11_USAGE_IMMUTABLE;vbd.ByteWidth = sizeof vertices;vbd.BindFlags = D3D11_BIND_VERTEX_BUFFER;vbd.CPUAccessFlags = 0;// 新建顶点缓冲区D3D11_SUBRESOURCE_DATA InitData;ZeroMemory(&InitData, sizeof(InitData));InitData.pSysMem = vertices;HR(m_pd3dDevice->CreateBuffer(&vbd, &InitData, m_pVertexBuffer.ReleaseAndGetAddressOf()));// 线框顶点数m_VertexCount = 41;
}
GameApp
类剩余部分可以在项目源码中查看。
最终效果如下:
DirectX11 With Windows SDK完整目录
Github项目源码
欢迎加入QQ群: 727623616 可以一起探讨DX11,以及有什么问题也可以在这里汇报。
转载于:https://www.cnblogs.com/lonelyxmas/p/10811380.html
DirectX11 With Windows SDK--15 几何着色器初探相关推荐
- DirectX11 With Windows SDK--17 利用几何着色器实现公告板效果
DirectX11 With Windows SDK--17 利用几何着色器实现公告板效果 原文:DirectX11 With Windows SDK--17 利用几何着色器实现公告板效果 前言 上一 ...
- Windows 8 Directx 开发学习笔记(十四)使用几何着色器实现三角形细分
几何着色器是从DirectX 10才引入的着色器,是一个可选阶段,位于顶点着色器和像素着色器阶段之间.顶点着色器以顶点作为输入数据,而几何着色器以完整的图元作为输入数据,像点.直线.三角形等.之所以引 ...
- DirectX11 使用几何着色器实现公告板效果
之前说过,可编程的着色器阶段有三个,依次是顶点着色器.几何着色器.像素着色器.之前说的都是顶点着色器和像素着色器,今天第一次详细介绍几何着色器,几何着色器是一个可选的阶段. 一.使用几何着色器的好处 ...
- UnityShader[4]几何着色器与可交互草地
GeometryShader执行顺序在顶点着色器之后,片元着色器以前.GeometryShader以一个/多个顶点组成的图元为输入,开发人员可以修改/添加顶点,修改为完全不同的网格,得到更多好看的效果 ...
- Unity之几何着色器--草随风摇曳
1. 简介12 1.1 主要工作: 通过HeightMap生成地形网格 通过分块的思想生成草的初始定位顶点 通过几何着色器配合伪随机数生成草的网格 通过伪随机函数来对风进行模拟 通过Blinn Pho ...
- 几何着色器与细分(镶嵌)着色器
几何着色器与细分(镶嵌)着色器 GL细分着色器:https://www.cnblogs.com/zenny-chen/p/4280100.html GL几何着色器:https://blog.csdn. ...
- OpenGL Tessellation and Geometry Shaders镶嵌和几何着色器的实例
OpenGL 镶嵌和几何着色器 先上图,再解答. 完整主要的源代码 源代码剖析 先上图,再解答. 完整主要的源代码 #include <sb7.h> class tessllatedgst ...
- OpenGL 几何着色器细分的实例
OpenGL 几何着色器细分 先上图,再解答. 完整主要的源代码 源代码剖析 先上图,再解答. 完整主要的源代码 #include <vmath.h> #include <cmath ...
- OpenGL 几何着色器剔除的实例
OpenGL 几何着色器剔除 先上图,再解答. 完整主要的源代码 源代码剖析 先上图,再解答. 完整主要的源代码 #include <vmath.h> #include <objec ...
最新文章
- OVS DPDK--虚拟设备配置(六)
- 佟年计算机天才不会打游戏,《亲爱的热爱的》佟年爱得卑微?其实是韩商言,爱得不够勇敢...
- NFS PersistentVolume - 每天5分钟玩转 Docker 容器技术(151)
- 查看apk包名和Activity的方法
- [react] 为什么属性使用className而不是class呢?
- [导入]实现删除主表数据时, 判断与之关联的外键表是否有数据引用, 有标志, 无则删除...
- 嵌入式数据库Sqlite移植教程-转
- 用SpringMVC参数传递时,解决get请求时中文乱码的问题
- 山东理工大计算机学院袁帅,山东理工大学学子讲坛暨第一届学生会学风建设交流会举办...
- JS中将json字符串转为json对象的三种方式
- 免费证件照换底色微信小程序
- 555555555555555555555
- Matlab/Simulink 自动代码生成 基于模型设计学习教程(2)---- 闪烁灯实验
- 普通路由器改4g路由器_4G工业路由器将加速工业物联网发展进程
- 平面设计新手如何用PS制作出一张合成海报
- java判断内网ip_Java判断IP地址为内网IP还是公网IP的方法
- UR机器人返回信息格式解析
- static定义静态方法
- 清晨为何不能空腹喝蜂蜜水
- Oracle物化视图的使用总结
热门文章
- 猎豹掌门人出走,傅盛的“梦游”该醒醒了
- Linux用wget和curl下载jdk
- 大型网站技术架构演进
- CAD删除数据库对象
- 什么是token?Android中token的使用讲解
- powerdesigner CDM中联系理解
- 学习笔记(05):区块链应用案例-区块链金融应用-保险
- 打印机服务器的系统,打印机服务器主机系统
- 稳坐全球第一的小米手环,爆款背后的“护城河”是如何造就的?
- Understanding C/C++ Strict Aliasing