有人写着色器3.0下载么

首先题目可能问得不太对,主要是我不太理解像素着色器这个东西。&br&&br&以前大学的毕业设计做一个和渲染有关的东西,当时没接触过shader。绘制操作是操作屏幕缓存(这个词这么说专业么),总之是取得了屏幕的buffer,往里面写数据。当时写一个像素成功了之后,就开始往下做了。后面绘制很多面三角形的时候已经非常卡了,可能和我写的代码结构有关系。虽然每帧lock和unlock也就各一次。依然无法阻止运行结果很卡。&br&&img src=&/eb28a2a9a1d1c2dc56a7c75e020a8054_b.jpg& data-rawwidth=&490& data-rawheight=&372& class=&origin_image zh-lightbox-thumb& width=&490& data-original=&/eb28a2a9a1d1c2dc56a7c75e020a8054_r.jpg&&后面看到shader的一些用法,其实一直没有理解像素shader这个东西。&br&有一个图:&br&&img src=&/5efcc63fffc51db84cdc1a4_b.jpg& data-rawwidth=&370& data-rawheight=&480& class=&content_image& width=&370&&&br&这是看rendermokey的时候看到的,这个图里我能理解DISPLAY就是present那个函数的时机么。像素着色器只是一个在像素写到缓存前能让程序员有机会用编程语言修改像素的地方么?还是说它的地位和写像素的缓存的操作的地位是一样的?&br&&br&导致我混乱的地方是,我毕业设计是基于一个像素操作的,而这个玩意的名字也和像素有关,我能换成基于像素shader(代替我以前往屏幕缓存写数据的方式)来绘制一些2D的点或者什么东西么?&br&&br&最后一个疑问是,如果只想试试一些2D的图形算法。是不是基于dx9.0的屏幕操作就可以了,还是说高版本的dx的屏幕操作更效率??或者换opengl?&br&&br&------------------看了评论之后补充新的疑问-----------------------------------&br&&a href=&/people/gu-jun-6& class=&internal&&谷俊&/a& ,&a href=&/people/Isaac-tang& class=&internal&&Isaac Tang&/a&的评论&br&没想到是CPU和GPU的差别。也就是说lock屏幕缓存,是CPU操作,shader是GPU操作。还有一点就是我完全不需要3D数据,或许可以说我想着我就是操作一个屏幕,操作的过程可能需要各种算法逻辑之类的。这时候用shader适合吗?因为看rendermonkey的教程里有一则是讲将渲染结果绘制到一个纹理目标上,然后再把纹理映射到一个“挡在屏幕前方的片上“,rendermonkey已经预制好了这个多边形的数据,只要拖出来就能用。这样让我觉得shader它虽然用了GPU,但是它已经过了很多3D的流程,至少是顶点转换‘多边形剔除那些过程等等。对于一个只关心2D效果的程序是不是多余了。如果只是做一些2D效果,就走了那么多shader必须走的3D数据到屏幕上的过程,有点多余的感觉。&br&奥,当然我是想有一个方便实验各种算法效果的环境和手段,当然也希望它不是在流程上冗余的。至少不要做一些和实验内容无关而事情。然后其效率当然是越快越好,抛开算法不说,是想找到一个好的实验方式。&br&&br&&a href=&/people/vczh-the-genius& class=&internal&&vczh&/a&的评论&br&看了你的评论,我觉得或许我应该看看D2D。D2D是正解吗?如果D2D也是操作的屏幕缓存,是不是也是CPU操作,然后就和我最初的方式是同一个路子?还是说D2D提供有更奇妙的操作屏幕像素的方式?&br&&br&&a href=&/people/jsn-bin& class=&internal&&jsn bin&/a&的评论&br&就是说如果和3D效果无关,就是拷贝一些东西到屏幕,必然要操作CPU吗?&br&&br&最后就是&br&cryengine3的渲染是单独开了一个线程来做,可以类似于此么。开个线程,让它去操作屏幕,而主线程只关心各种算法和数据。因为渲染线程只是读数据,主线程写数据,至少可以拷贝一个数据副本给渲染线程都可以啊,也没有什么风险的样子。能提高速度么。&br&其实蛮想用shader的,就是不知道能否避开繁琐的3D数据需要经过的流程。
首先题目可能问得不太对,主要是我不太理解像素着色器这个东西。以前大学的毕业设计做一个和渲染有关的东西,当时没接触过shader。绘制操作是操作屏幕缓存(这个词这么说专业么),总之是取得了屏幕的buffer,往里面写数据。当时写一个像素成功了之后,就开始往下做了。后面绘制很多面三角形的时候已经非常卡了,可能和我写的代码结构有关系。虽然每帧lock和unlock也就各一次。依然无法阻止运行结果很卡。后面看到shader的一些用法,其实一直没有理解像素shader这个东西。…
Update 题主的继续提问。我很理解楼主的想法,一物对一物,杀鸡不用牛刀。不过从软件工程的角度来讲,你犯了一个错误,叫做过早优化。当然,如果我清楚系统的各个组件什么情况下快,什么情况下慢,一开始实施的时候就尽可能的让每个组件都快,这样是最好的。但是,优化这件事情还是要放到最后做的,因为它是一个系统性的工程,不是每个组件都最快,系统就最快的。答案是要到最后才会揭晓的。回答题主的具体问题。首先shader这个概念就是在3D的上下文诞生的。要有光照变换才会有“影”(shade)嘛(其实是瞎编的)。如果你写过D3D应用,应该还记得Vertex Shader, Pixel Shader, Geometry Shader都是在挂在D3D context下的。另外不清楚你具体要实验的到底是什么,不管是什么,它总跟“顶点”还是密切相关的。对“画素”的操作不管有多简单,总是要有“顶点”的信息的。如果你说,我这操作就没“顶点”啥事。那其实你可以考虑Compute Shader,用GPU做通用计算就好了,最后的显示过程其实是次要的问题,输出位图,直接写fb,或者用Win32去画,或者再用D3D去画都可以。我猜测题主最终还是要把你的算法结合到一个大的系统中,想必是3D应用。所以还是乖乖的写3D吧。回到效率问题。最早期的3D显卡,3D、2D都是专门的硬件,有可能这块显卡,2D性能超强,3D性能孱弱。有针对性的使用2D而不用3D,是有可能更高效的。不过现在的显卡全都是Unified Shader,连VS,PS都不是专用硬件,管你2D 3D 通用计算甚至Video(大部分的Video还是有专用ASIC电路的),全都是在Unified Shader上执行的。所以OGL也好D3D也好,CUDA也好,硬件做的事情都一样多,区别就在于驱动层的厚度。直接测试算法你可以试试3DS MAX,它可以直接对模型应用Shader。或者你只关心Pixel Shader的话,比如MediaPlayer Classic有这样一个有趣的功能。可以即时反馈效果。-----------------------------------------------------------------“直接往屏幕缓存写像素数据”。我没理解错的话,你是lock framebuffer,然后直接操作map出来的buffer?这样的话,1,GPU被挡住了;2,后续操作是CPU在做,通常情况下,CPU访问显存会比它访问内存慢,当然也比GPU访问显存慢多了。“使用像素着色器”。顾名思义,你写了一个shader,然后让GPU去执行。并行性,显存访问速度都有保证。两者对比,显而易见了。实际上你的问题很简单,你没有理解Pixel Shader是GPU去执行的,并不是CPU。最后的问题,你自己实现图形算法,从不同的目的来看,1,如果你是要看它的效果,不关心它的速度,那什么DX9什么的毫无关系,直接lock framebuffer,然后该干嘛干嘛。或者甚至直接用MatLab什么的做更方便简单。2,如果你是同时要求速度,那首先还是把第一步做了,然后写Pixel Shader了。至于要什么版本的DX,那就看你的Shader有多复杂了,比如DX9.0c 对应Pixel Shader version 3.0。不同的PS版本,限制不同,比如寄存器数量,是否可以有特定的操作,贴图数量什么的。
最近在写一个简单的软件渲染器,来说下,不对的地方请大家指正。现代GPU已经抛弃了早期固定渲染管线了,取而代之的是可编程渲染管线,也就是大家常见到的顶点着色器和片段着色器等。放一张图,来自OPENGL官网:一般来说我们要表示一个物体时,通常用点和面来对它进行建模,这个是一些CAD软件的工作。物体由许多顶点构成,我们在CPU端告诉GPU这些数据是怎么表示的,并将相应的数据发送给显存。在GPU端,Vertex Shader 就起作用了,我们写好自己的顶点着色器程序,GPU将并行的对于每一个顶点进行我们想要的操作(如投影变换等)。后续的工作有图元组装(将点组装成三角形面等基本图元),裁剪和隐藏面消除(在我们对应的视见体之外的部分裁剪掉不显示),光栅化(将每一个基本图元转化成一堆像素)在光栅化之后我们将对产生的这些像素进行操作,这个时候就是Fragment Shader(Pixel Shader)的作用了。我们写好自己的片段着色器程序,每个像素都会调用这段程序(并行的),然后通过zbuffer算法确定最后的像素的颜色,赋值给对应的帧缓存区就可以了。知道了这些应该就比较明了了,针对楼主的问题,像素着色器只是一个在像素写到缓存前能让程序员有机会用编程语言修改像素的地方么?是。我能换成基于像素shader(代替我以前往屏幕缓存写数据的方式)来绘制一些2D的点或者什么东西么?Pixel Shader针对光栅化之后的像素操作, 它的输入包含了当前像素在窗口坐标系下的位置,还有一些其他的插值之后的量。换句话说,你可以修改这个位置对应像素的颜色,但是你不能修改它的位置。注意:在GPU中是并行执行的。所以对点的操作应该放在顶点着色器。如果只想试试一些2D的图形算法。是不是基于dx9.0的屏幕操作就可以了,还是说高版本的dx的屏幕操作更效率??或者换opengl?以前翻过一眼SFML这个库的画线函数,发现就是调用Opengl,所以我觉得针对2D的图形算法,轮子哥说的Direct2D应该比较合适...
这是一个long story,得找个有耐心的大牛来回答,比如 Milo 大大。1.3怎么写HLSL着色器
1.3怎么写HLSL着色器
我们可以直接把HLSL着色器代码作为一长串字符串编写进我们的应用程序源文件中,但是,更加方便和模块化的方法是把着色器的代码从应用程序代码中分离出来。因此,我们将着色器代码单独保存为文本格式,然后在应用程序中使用特定函数将其加载进来。
下面是一个完整的HLSL着色器程序代码,我们把它保存在BasicHLSL.txt中。该着色器完成顶点的世界变换、观察变换和投影变幻,并将顶点颜色设定为指定的颜色。
// BasicHLSL.txt
// Global variable
matrix WVPM
// Structures
struct VS_INPUT
vector position : POSITION;
struct VS_OUTPUT
vector position : POSITION;
vector color : COLOR;
// Functions
VS_OUTPUT SetColor(VS_INPUT input)
VS_OUTPUT output = (VS_OUTPUT)0;
output.position = mul(input.position, WVPMatrix);
output.color =
下面就针对上述代码讲解一下HLSL着色器程序的编写:
1.3.1全局变量
代码中声明了两个全局变量:
matrix WVPM
变量WVPMatrix是一个矩阵类型,它包含了世界、观察、投影的合矩阵,用于对顶点进行坐标变换;
变量color是一个向量类型,它用于设定顶点颜色;
代码中并没有对全局变量进行初始化,这是因为我们对全局变量的初始化过程将在应用程序中进行,全局变量在应用程序中赋值而在着色器程序中使用,这是应用程序和着色器通信的关键所在。具体赋值过程将在后续部分讲述。
1.3.2输入输出
? 输入输出结构
程序中定义了两个输入输出结构VS_INPUT和VS_OUTPUT
struct VS_INPUT
vector position : POSITION;
struct VS_OUTPUT
vector position : POSITION;
vector color : COLOR;
自定义的结构可以采用任意名称,结构不过是一种组织数据的方式,并不是强制的,你也可以不使用,而将本程序的输入改为:
vector position : POSITION;
用于输入输出的变量采用用一种特殊的声明方式:
Type VariableName : Semantic
这个特殊的冒号语法表示一个语义,冒号后面的标志符用来指定变量的用途,如
vector position : POSITION;
其中,POSITION标志符表明该变量表示顶点位置,另外还有诸如COLOR、NORMAL等很多表示其他意义的标志符。
本节所说的输入输出其实是指着色器代码和编译器、GPU之间的通信,和应用程序是无关的,所以这些变量不需要在应用程序中进行赋值,标志符告诉编译器各个输入输出变量的用途(顶点位置、法线、颜色等),这是着色器代码和编译器、GPU之间通信的关键。
1.3.3入口函数
程序中还定义了一个函数SetColor:
OUTPUT SetColor(INPUT input)
VS_OUTPUT output = (VS_OUTPUT)0;
output.position = mul(input.position, WVPMatrix);
output.color =
1. 该函数以input和output类型作为输入输出;
2. 使全局变量WVPMatrix和input.position相乘,以完成顶点的世界、观察、投影变换,并把结果赋值到output.position;
output.position = mul(input.position, WVPMatrix);
3. 将全局变量color的值赋给output.color;
output.color =
4. 在同一个着色器代码文件中,可以有多个用户自定义函数,因此在应用程序中需要指定一个入口函数,相当于windows程序的WinMain函数,本程序只包含SetColor一个函数而且它将被做为入口函数使用。
至此,一个HLSL着色器编写完毕,渲染过程中,当一个顶点被送到着色器时:
1. 全局变量WVPMatrix、color将在应用程序中被赋值;
2. 入口函数SetColor被调用编译器根据标志符将顶点信息填充到VS_INPUT中的各个字段;
3. SetColor函数中,首先定义一个VS_OUTPUT信息,之后根据WVPMatrix和color变量完成顶点的坐标变换和颜色设定操作,最后函数返回VS_OUTPUT结构;
4. 编译器将会再次根据标志符把返回的VS_OUTPUT结构中的各字段映射为顶点相应的信息。
5. 顶点被送往下一个流程接受进一步处理。
上述过程中,全局变量在应用程序中赋值而在着色器程序中使用,这是应用程序和着色器通信的关键所在;标志符告诉编译器各个输入输出变量的用途(顶点位置、法线、颜色等),这是着色器代码和编译器、GPU之间通信的关键。个人认为这是着色器中最为精义的地方:)
文章评论 以下网友留言只代表其个人观点,不代表本网站的观点和立场。使用 GLSL 写入 WebGL 着色器 (Windows)
使用 GLSL 写入 WebGL 着色器
学习编写 WebGL 着色器来创建 GPU 中直接运行的高性能 2D 和 3D 图形。
着色器和变量
着色器使用 GLSL 编写,它是一种类似 C 的代码,在 GPU 中运行并操作你在 JavaScript 中创建的顶点和纹理数据。有两种类型的着色器:
顶点着色器,适用于顶端缓冲区中包含的坐标数据
片段着色器,用于确定每个像素的颜色
着色器源代码使用 &script& 标记在 HTML 文件中定义,或在 JavaScript 代码中定义为字符串。
着色器是呈现管道的可编程部分,既可能简单,也可能复杂,具体取决于你希望达到的效果。
与在 JavaScript 中不同,GLSL 变量是类型化的。在创建变量时,不是使用 var,而是使用 type,如在 C++ 或 C# 中那样。有几种类型,如:int、float、vec 或 mat。还有一些限定符(如 uniform、attribute 和 varying)用于设置变量的用法。为类型化变量分配编号时,必须使用正确的注释。例如,6 和 6.0 在 JavaScript 中实质上是一样的。而在 GLSL 中,他们的类型分别是 int 和 float。你可以规定 int x = 6 或 float x = 6.0,但不能规定 float x = 6。
JavaScript 代码中的数据通过两种类型的限定符变量来传递:uniform 和 attribute。
uniform 变量是全局变量。这些变量可由顶点着色器或片段着色器使用,定义在程序运行过程中保持不变的值。缩放顶点所使用的值就是此类变量的一个例子。
attribute 变量是属于特定顶点的变量。attribute 变量只能在顶点着色器中使用,可用于为每个顶点设置特定颜色。
还有第三个限定符变量,即 varying 变量,只在 GLSL 着色器代码中声明。varying 变量在顶点着色器中设置,并在片段着色器中使用。
每个着色器的输出是通过特殊变量传递的。对于顶点着色器,当前顶点的位置使用 gl_Position 传递到片段着色器。gl_Position 变量是一个四维 (vec4) 变量,包含顶点的 x、y、z 和 w 值。
片段着色器的输出是 gl_FragColor,它是一个四维变量(或称为 vec4)。gl_FragColor 表示在经过着色器代码处理后,正在呈现的像素的 R、G、B、A 值。
构建顶点着色器和片段着色器
在此示例中,创建了两个着色器程序,一个包含纹理(或照片),一个包含红色片段着色器。根据 UI 内选中的是“显示三角形网格”还是“显示呈现的照片”单选按钮,将激活这两个程序中的某一个。
// Load the GLSL source written in the HTML file.
// Create a program with the two shaders
this.lineprogram = loadProgram(gl, getShader(gl, "2d-vertex-shader"), getShader(gl, "red"));
// Tell webGL to use this program
gl.useProgram(this.lineprogram);
这部分代码调用函数用于创建顶点着色器和片段着色器,以及创建着色器程序。 返回时, 会激活刚刚创建的着色器程序。
在 Warp 示例中,顶点着色器和片段着色器是使用 getShader 函数创建的。
// Loads a shader from a script tag
// Parameters:
WebGL context
id of script element containing the shader to load
function getShader(gl, id) {
var shaderScript = document.getElementById(id);
// error - element with supplied id couldn't be retrieved
if (!shaderScript) {
// If successful, build a string representing the shader source
var str = "";
var k = shaderScript.firstC
while (k) {
if (k.nodeType == 3) {
str += k.textC
k = k.nextS
// Create shaders based on the type we set
note: these types are commonly used, but not required
if (shaderScript.type == "x-shader/x-fragment") {
shader = gl.createShader(gl.FRAGMENT_SHADER);
} else if (shaderScript.type == "x-shader/x-vertex") {
shader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(shader, str);
gl.compileShader(shader);
// Check the compile status, return an error if failed
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
console.log(gl.getShaderInfoLog(shader));
getShader 函数传递到
对象和着色器脚本元素的 id。getShader 遵循以下步骤并返回一个片段着色器或顶点着色器:
和 id 属性可以从 HTML 的脚本标记中获取着色器源代码。
GetShader 从着色器代码中构建字符串 (str),每次构建一个文本节点。函数将持续运行,直至文本用尽。
着色器的类型可通过在 &script& 元素上使用 type 属性进行识别。脚本标记的 type 属性 (x-shader/x-fragment, x-shader/x-vertex) 不属于任何标准,将被 HTML 忽略。但是,你仍然可以在 JavaScript 中使用 type 属性来读取它们。
根据类型,将使用
创建空的片段着色器对象或顶点着色器对象。
和之前创建的着色器源代码字符串来添加源代码。
着色器对象将使用
进行编译。
和 COMPILE_STATUS 标志检查编译状态。
如果 COMPILE_STATUS 标志为 false,那么着色器便无法编译,同时 getShader 返回 NULL。
如果 COMPILE_STATUS 标志为 true,那么 getShader 将返回着色器对象。
为了确保返回的着色器成功连接到程序对象,我们应结合使用
方法和 gl.LINK_STATUS 常量以获取状态。如果着色器正确链接到程序对象,则此方法返回 true,否则返回 false。如果着色器没有正确链接,则使用
检索最后一个错误,并显示在控制台中。如果存在错误,则程序会被 WebGL 删除。如果没有错误,则返回程序对象。
创建着色器程序
Warp 示例会使用 loadprogram 函数创建一个着色器程序对象,并将使用 getShader 函数创建的顶点着色器和片段着色器相连接。
着色器程序链接顶点着色器和片段着色器,并在 GPU 上运行。它包含在 CPU 上的 JavaScript 和 GPU 上的 JavaScript 之间传递数据所需的变量。如 WebGL 中的众多进程一样,创建和使用着色器程序需要几个步骤。下面是 Warp 示例中的 loadProgram 函数。loadProgram 函数将创建着色器程序,并链接顶点着色器和片段着色器。
function loadProgram(gl, vertexShader, fragmentShader)
// create a progam object
var program = gl.createProgram();
// attach the two shaders
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
// link everything
gl.linkProgram(program);
// Check the link status
var linked = gl.getProgramParameter(program, gl.LINK_STATUS);
if (!linked) {
// An error occurred while linking
var lastError = gl.getProgramInfoLog(program);
console.warn("Error in program linking:" + lastError);
gl.deleteProgram(program);
// if all is well, return the program object
loadProgram 运行时将遵循以下步骤:
创建一个包含
的程序对象。
将传递到 loadProgram 的顶点着色器和片段着色器进行连接。
链接着色器和着色器程序。
检查链接状态,同时传递着色器程序和 LINK_STATUS 标志。
如果链接状态为 false,则使用 getProgramInfoLog 获取最后一个错误并打印到控制台。使用 deleteProgram 删除程序,并返回 NULL。
如果链接状态为 true,则返回着色器程序。
将 JavaScript 中的数据传递到着色器中
虽然已创建着色器代码,但我们仍需要向其提供数据以创建自己的图形。矢量数据通过 attribute 变量从 JavaScript 传递到着色器。着色器可获取多种类型的属性,这些类型包括 vec2、vec3、vec4、bool 和 int 等等。这些属性在着色器中进行声明,通过绑定着色器程序中的缓冲区从 JavaScript 进行访问。要访问着色器变量,需要了解其位置。attribute 变量 a_texCoord 的位置在着色器中声明,并使用
// Look up where the vertex data needs to go.
this.texCoordLocation2 = gl.getAttribLocation(this.lineprogram, "a_texCoord");
// Provide texture coordinates for the rectangle.
this.texCoordBuffer2 = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, this.texCoordBuffer2);
// Create a buffer and set it use the array set up above.
// Set it to be modified once, use many.
// createRedGrid sets up the vector array itself.
gl.bufferData(gl.ARRAY_BUFFER, createRedGrid(), gl.STATIC_DRAW); // Fill buffer data
// Turns on the vertex attributes in the GPU program.
gl.enableVertexAttribArray(this.texCoordLocation2);
// Set up the data format for the vertex array - set to points (x/y).
// Use floats.
gl.vertexAttribPointer(this.texCoordLocation2, 2, gl.FLOAT, false, 0, 0);
此部分代码遵循以下步骤:
创建缓冲区来存储矢量数组。
将矢量数组绑定到着色器程序。绑定缓冲区可将其激活,并后续对
进行调用。
然后,通过 ,用我们的矢量数组来填充缓冲区。gl.STATIC_DRAW 标志告知 WebGL,此缓冲区数据将一次写入,但多次使用。
启用顶点属性。这将告知着色器程序将该缓冲区作为活动缓冲区使用。
最后,WebGL 需要知道使用
的数组格式。此方法将传递数组位置 texCoordLocation2、数组中每个元素的维数 (2) 以及数据类型 (gl.FLOAT)。其余参数是默认值,且 normalized 设置为 false,stride 和 offset 均设置为 0。
如果成功完成这些步骤,则表示着色器程序已经为我们构建的形状的矢量点设置了一个缓冲区。接下来的部分我们将详细讨论一下着色器代码本身。
着色器代码
着色器程序通过顶点数组一次对一个顶点进行操作,从而对网格进行更改。传入顶点传递到包含 a_texCoord 属性的顶点着色器,并作为一个 varing v_texCoord 变量从顶点着色器传递到片段着色器。在此处的示例中,两个变量均为二维矢量,但它们可以是任意类型。
顶点着色器中已计算的 x,y 坐标的输出将通过 gl_Position 传递到片段着色器。gl_Position 是一个 vec4 变量,在 Warp 演示中表示 x 和 y 坐标。将坐标系中心和最后的 z 坐标设置为 0,或者将 w 参数设置为 1。w 参数与投影矩阵结合使用。 将 x、y 和 z 坐标与投影矩阵相乘,将该点投影或该点放置到 3D 空间中。设置为 1 时,将不会投影 x、y 和 z 坐标。
将为移动拖动的开始和结束更改 uniform 变量。
&script id="2d-vertex-shader" type="x-shader/x-vertex"&
// outgoing coordinate
varying vec2 v_texC
// incoming coordinate (point)
attribute vec2 a_texC
// maximum number of changes to grid
#define MAXPOINTS 10
uniform vec2 p1[MAXPOINTS];
// Where the drag started
uniform vec2 p2[MAXPOINTS];
// Where the drag ended
void main() {
v_texCoord = a_texC
// Set up position variable with current coordinate normalized from 0 - 1 to -1 to 1 range
vec2 position = a_texCoord * 2.0 - 1.0;
for (int i = 0; i & MAXPOINTS; i++) // loop through
float dragdistance = distance(p1[i], p2[i]); // Calculate the distance between two start and end of mouse drag for each of the drags
float mydistance = distance(p1[i], position);
// Calculate the distance between the start of the mouse drag and the last position
if (mydistance & dragdistance)
vec2 maxdistort = (p2[i] - p1[i]) / 4.0;
// only affect vertices within 4 x the drag distance (
float normalizeddistance = mydistance /
float normalizedimpact = (cos(normalizeddistance*3.)+1.0)/2.0;
position += (maxdistort * normalizedimpact);
// gl_Position always specifies where to render this vector
gl_Position = vec4(position, 0.0, 1.0);
在照片或网格上拖动鼠标光标时,鼠标事件将返回鼠标的坐标。坐标从基于像素的坐标转换为 -1 到 1 之间的值,并且加载到数组 (p1, p2) 中。将根据点击的坐标与拖动 (p1, p2) 之间的差值来重新计算网格顶点。这些值由 uniform 变量来传递。在此示例中,扭曲的区域限制为在着色器代码中计算的附近点的圆形区域。受影响圆形的大小取决于拖动的长度。在网格扭曲时,照片或纹理将更新其像素以产生弯曲效果。
有两种片段着色器,一种使用照片,另一种提供红色网格线。片段着色器是否活动取决于你设置的模式(网格或图像单选按钮)。
&!-- fragment shader --&
&script id="2d-fragment-shader" type="x-shader/x-fragment"&
precision mediump float;
// uniform to use for texture
uniform sampler2D u_
// Output of the vertex shader
varying vec2 v_texC
void main() {
// gl_FragColor always specifies the color to make the current pixel
gl_FragColor = texture2D(u_image, v_texCoord);
片段着色器比顶点着色器要稍微简单一些。片段着色器用于确定像素的颜色。可通过它从顶点着色器所接收的 varying 变量或纯色进行确定。 在网格的片段着色器中,将声明传入的 varying 变量,但会将其忽略。着色器只会将输出颜色设置为一个固定值,从不使用变量。
对于加载图像的片段着色器,将定义一个称为 u_image 的 uniform sampler2D 变量,以接受传入的像素。我们可以在加载该图像的 JavaScript 初始化代码中看到此 u_image 变量。u_image 的数据类型是指定数据为 2D 纹理的 sampler2D。
顶点着色器使用名为 v_texCoord 的 varying 变量,将指向此纹理的引用传递给片段着色器。v_texCoord 变量包含一个映射,该映射指向的图像纹理就是当前正在处理的坐标要获取的颜色来源。在红色着色器中,将声明 v_texCoord 变量,但是从不使用,且像素的传出值设为 red。所有行均以相同颜色着色,而不会扩展颜色,也不会进行混合。
不管片段着色器的输出颜色是来自某个纹理还是单个颜色,它都会被分配 gl_FragColor 变量。gl_FragColor 变量是一个标准输出参数,告知呈现缓冲区为当前像素使用什么颜色。
图像片段着色器使用 GLSL 函数 texture2D 进行输出。命令 gl_FragColor = texture2D(u_image, v_texCoord) 使用 texture2D 函数查找使用由 v_texCoord 指定坐标的纹理 (u_image),并将其传递给 gl_FragColor。然后,GPU 处理并显示该像素。
使用着色器程序
上面介绍的 loadProgram 函数让我们回顾了示例中的 init 函数,其中变量 lineprogram 和 pictureprogram 绑定到上下文对象。
在呈现函数中,着色器程序与缓冲区和 uniform 数据绑定,并且呈现。以下示例表明了程序、数据和属性如何相互连接和呈现。
Clear color buffer and set it to light gray
gl.clearColor(1.0, 1.0, 1.0, 0.5);
gl.clear(this.gl.COLOR_BUFFER_BIT);
// This draws either the grid or the photo for stretching
if (document.getElementById("renderLines").checked)
gl.bindBuffer(gl.ARRAY_BUFFER, this.texCoordBuffer2);
gl.useProgram(this.lineprogram);
gl.uniform2fv(gl.getUniformLocation(this.lineprogram, "p1"), p1);
gl.uniform2fv(gl.getUniformLocation(this.lineprogram, "p2"), p2);
gl.vertexAttribPointer(this.texCoordLocation2, 2, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(this.texCoordLocation2);
gl.drawArrays(gl.LINES, 0, resolution * resolution * 10);
gl.bindBuffer(gl.ARRAY_BUFFER, this.texCoordBuffer);
gl.useProgram(this.pictureprogram);
gl.uniform2fv(gl.getUniformLocation(this.pictureprogram, "p1"), p1);
gl.uniform2fv(gl.getUniformLocation(this.pictureprogram, "p2"), p2);
gl.vertexAttribPointer(this.texCoordLocation, 2, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(this.texCoordLocation);
gl.drawArrays(gl.TRIANGLES, 0, resolution * resolution * 2 * 3);
if/then 条件语句检查用户是希望显示照片还是网格,并加载相应的着色器程序。用于线条和照片呈现的代码是相同的,区别只是加载的着色器程序以及为
方法提供的绘制内容描述(三角形或线条)。以下是它的工作原理:
绑定顶点缓冲区以用于程序。
设置 lineprogram 或 pictureprogram 以用于当前呈现状态。
为程序分配起始点和结束点数组变量。
设置顶点缓冲区的格式和位置。
启用顶点缓冲区数组。
最后,利用 ,使用顶点数组和纹理作为线条或三角形来绘制程序。
上述是示例中着色器代码的总结。本示例和主题简化了创建着色器的过程,只涉及基本内容。有关着色器的更多深入信息,请参阅 。加载照片的相关内容包含在中。
为了帮助了解有关创建着色器程序的详细信息,请查看
网站上的。它在顶点和片段着色器中提供了大量用于实验的模板和网格。
您对此内容的反馈非常重要。请告诉我们您的想法。
更多反馈?
1500 个剩余字符
我们非常感谢您的反馈。
开发人员中心

我要回帖

更多关于 着色器2.0 的文章

 

随机推荐