Shapes(形状)

终于!我们一直学习的技能就等着这一刻!你已经学习过GLSL的大部分基础,类型和函数。你一遍又一遍的练习你的造型方程。是时候把他们整合起来了。你就是为了这个挑战而来的!在这一章里,你会学习到如何以一种并行处理方式来画简单的图形。

前面章节

  1. TheBookofShader开始
  2. 造型函数
  3. 颜色

长方形

想象我们有张数学课上使用的方格纸,而我们的作业是画一个正方形。纸的大小是10 x 10而正方形应该是8 x 8。你会怎么做?

你是不是会涂满除了第一行第一列和最后一行和最后一列的所有格点?

这和着色器有什么关系?方格纸上的每个小方形格点就是一个线程(一个像素)。每个格点有它的位置,就想棋盘上的坐标一样。在之前的章节我们将x和y映射到rgb通道,并且我们学习了如何将二维边界限制在0和1之间。我们如何用这些来画一个中心点位于屏幕中心的正方形?

我们从空间角度来判别的 if 语句伪代码开始。这个原理和我们思考方格纸的策略异曲同工。

if ( (X GREATER THAN 1) AND (Y GREATER THAN 1) )paint whiteelsepaint black

现在我们有个更好的主意让这个想法实现,来试试把if语句换成step(),并用0到1代替10 x 10的范围。

uniform vec2 u_resolution;void main(){vec2 st = gl_FragCoord.xy/u_resolution.xy;vec3 color = vec3(0.0);// Each result will return 1.0 (white) or 0.0 (black).float left = step(0.1,st.x);   // Similar to ( X greater than 0.1 )float bottom = step(0.1,st.y); // Similar to ( Y greater than 0.1 )// The multiplication of left*bottom will be similar to the logical AND.color = vec3( left * bottom );gl_FragColor = vec4(color,1.0);
}

step()函数会让没每一个小于0.1的像素变成黑色(vec3(0.0))并将其余的变成白色(vec3(1.0))。left 乘 bottom 效果相当于逻辑 AND —— 当 x y 都为 1.0 时乘积才能是 1.0。这样做的效果就是画了两条黑线,一个在画布的底边另一个在左边。

实际效果图

在前一例代码中我们重复每个像素的结构(左边和底边)。我们可以把原来的一个值换成两个值直接给step()来精减代码。就像这样:

vec2 borders = step(vec2(0.1),st);
float pct = borders.x * borders.y;

目前为止,我们只画了长方形的两条边(左边和底面)。看下下面的例子:

// Author @patriciogv - 2015
// http://patriciogonzalezvivo.com#ifdef GL_ES
precision mediump float;
#endifuniform vec2 u_resolution;
uniform vec2 u_mouse;
uniform float u_time;void main(){vec2 st = gl_FragCoord.xy/u_resolution.xy;vec3 color = vec3(0.0);// bottom-leftvec2 bl = step(vec2(0.1),st);float pct = bl.x * bl.y;// 这里1.0-st就是把所有大于0.9像素的变成黑色// vec2 tr = step(vec2(0.1),1.0-st);// pct *= tr.x * tr.y;color = vec3(pct);gl_FragColor = vec4(color,1.0);
}

取消 top-right下面两句代码的注释来看看如何转置坐标的同时重复使用 step() 函数。这样二维向量 vec2(0.0,0.0) 会被变换到右上角。这就是转置页面和重复过程的数字等价。

实际效果图

注意这里,所有的边(宽)都被放大了。等价于这样写:

  vec2 bl = step(vec2(0.1),st);       // bottom-leftvec2 tr = step(vec2(0.1),1.0-st);   // top-rightcolor = vec3(bl.x * bl.y * tr.x * tr.y);

是不是很有趣?这种都是关于运用 step() 函数、逻辑运算和转置坐标的结合。

再进行下一个环节之前,挑战下下面的练习:

  • 改变长方形的比例和大小。
  • 用 smoothstep() 函数代替 step() 函数,试试在相同的代码下会有什么不同。注意通过改变取值,你可以不仅可以得到模糊边界也可以由漂亮的顺滑边界。
  • 应用 floor() 做个另外的案例。
  • 挑个你最喜欢的做成函数,这样未来你可以调用它。并且让它灵活高效。
  • 写一个只画长方形四边的函数。

在笛卡尔坐标系下,用方格纸来画正方形和长方形是很容易的。但是画圆就需要另一种方式了,尤其我们需要一个对“每个像素”的算法。一种解决办法是用step()函数将重新映射的空间坐标来画圆。

如何实现?让我们重新回顾一下数学课上的方格纸:我们把圆规展开到半径的长度,把一个针脚戳在圆圆心上,旋转着把圆的边界留下来。

将这个过程翻译给 shader 意味着纸上的每个方形格点都会隐含着问每个像素(线程)是否在圆的区域以内。我们通过计算像素到中心的距离来实现(这个判断)。

There are several ways to calculate that distance. The easiest one uses the distance() function, which internally computes the length() of the difference between two points (in our case the pixel coordinate and the center of the canvas). The length() function is nothing but a shortcut of the hypotenuse equation that uses square root (sqrt()) internally.

有几种方法来计算距离。最简单的是用distance()函数,这个函数其实内部调用 length()函数,计算不同两点的距离(在此例中是像素坐标和画布中心的距离)。length()函数内部只不过是用平方根(sqrt())计算斜边的方程。

你可以使用distance(), length() 或 sqrt()到计算屏幕的中心的距离。下面的代码包含着三个函数,毫无悬念的他们返回相同的结果。

注释和取消某行的注释来试试看用不同方式得到相同的结果。

#ifdef GL_ES
precision mediump float;
#endifuniform vec2 u_resolution;
uniform vec2 u_mouse;
uniform float u_time;void main(){vec2 st = gl_FragCoord.xy/u_resolution;float pct = 0.0;// a. The DISTANCE from the pixel to the centerpct = distance(st,vec2(0.5));// b. The LENGTH of the vector//    from the pixel to the center// vec2 toCenter = vec2(0.5)-st;// pct = length(toCenter);// c. The SQUARE ROOT of the vector//    from the pixel to the center// vec2 tC = vec2(0.5)-st;// pct = sqrt(tC.x*tC.x+tC.y*tC.y);vec3 color = vec3(pct);gl_FragColor = vec4( color, 1.0 );
}

上回我们把到中心的距离映射为颜色亮度。离中心越近的越暗。注意到映射值不宜过高,因为从中心(vec2(0.5, 0.5))到最远距离才刚刚超过0.5一点。仔细考察这个映射:

  • 你能从中推断出什么?
  • 我们怎么用这个方法来画圆?
  • 试试有没有其他方法来实现这样画布内圆形渐变的效果。

距离场

我们可也可以从另外的角度思考上面的例子:把它当做海拔地图(等高线图)——越黑的地方意味着海拔越高。想象下,你就在圆锥的顶端,那么这里的渐变就和圆锥的等高线图有些相似。到圆锥的水平距离是一个常数0.5。这个距离值在每个方向上都是相等的。通过选择从那里截取这个圆锥,你就会得到或大或小的圆纹面。

其实我们是通过“空间距离”来重新解释什么是图形。这种技巧被称之为“距离场”,从字体轮廓到3D图形被广泛应用。

来小试下牛刀:

  • 用step()函数把所有大于0.5的像素点变成白色,并把小于的变成黑色(0.0)。
  • 反转前景色和背景色。
  • 调戏下smoothstep()函数,用不同的值来试着做出一个边界顺滑的圆。
  • 一旦遇到令你满意的应用,把他写成一个函数,这样将来就可以调用了。
  • 给这个圆来些缤纷的颜色吧!
  • 再加点动画?一闪一闪亮晶晶?或者是砰砰跳动的心脏?(或许你可以从上一章汲取一些灵感)
  • 让它动起来?能不能移动它并且在同一个屏幕上放置多个圆?
  • 如果你结合函数来混合不同的距离场,会发生什么呢?
pct = distance(st,vec2(0.4)) + distance(st,vec2(0.6));
pct = distance(st,vec2(0.4)) * distance(st,vec2(0.6));
pct = min(distance(st,vec2(0.4)),distance(st,vec2(0.6)));
pct = max(distance(st,vec2(0.4)),distance(st,vec2(0.6)));
pct = pow(distance(st,vec2(0.4)),distance(st,vec2(0.6)));
  • 用这种技巧制作三个元素,如果它们是运动的,那就再好不过啦!

下面5个图是上面5句代码的效果

学习延伸

PS:关于距离场的内容,这里还有个更详细的解释:距离场

添加自己的工具箱

就计算效率而言,sqrt()函数,以及所有依赖它的运算,都耗时耗力。dot()点乘是另外一种用来高效计算圆形距离场的方式。

// Author @patriciogv - 2015
// http://patriciogonzalezvivo.com#ifdef GL_ES
precision mediump float;
#endifuniform vec2 u_resolution;
uniform vec2 u_mouse;
uniform float u_time;float circle(in vec2 _st, in float _radius){vec2 dist = _st-vec2(0.5);return 1.-smoothstep(_radius-(_radius*0.01),_radius+(_radius*0.01),dot(dist,dist)*4.0);
}void main(){vec2 st = gl_FragCoord.xy/u_resolution.xy;vec3 color = vec3(circle(st,0.9));gl_FragColor = vec4( color, 1.0 );
}

这里解释下为什么这样可以画圆。dot(dist,dist)=dist.xdist.x+dist.ydist.y;根据上面的_st-vec2(0.5)我们能够算出来dist的x,y范围在【0-0.5】之间。我们可以取值上下左右4个点的top(0.0,-0.0),bottom(0.0,0.5),left(-0.5,0),right(0.5,0),那么dot(dist,dist)4就是(0.50.5+0)*4=1。根据smoothstep这个方法的定义我们知道,如果你的_radius是1的话,只要是小于1的部分都是取值0,大于1的部分都1,中间差值。这样大家应该能知道为什么能做圆了吧。图形上的每个点都可以这样算出来。如果你的半径_radius=0.5的话,那么你可以算下上下左右4个点的坐标然后算出具体值,代进去也不难算出来,圆上的每个点也是如此。(当时我纠结这个纠结了很久,可能是因为我自己比较蠢吧,哈哈哈哈)

距离场的特点

距离场几乎可以用来画任何东西。显然,图形越复杂,方程也越复杂。但是一旦你找到某个特定图形的公式,就很容易添加图形或应用像过渡边界的效果。正因如此,距离场经常用于字体渲染,例如Mapbox GL Labels, Matt DesLauriers Material Design Fonts 和 as is describe on Chapter 7 of iPhone 3D Programming, O’Reilly.

看看下面的代码:

#ifdef GL_ES
precision mediump float;
#endifuniform vec2 u_resolution;
uniform vec2 u_mouse;
uniform float u_time;void main(){vec2 st = gl_FragCoord.xy/u_resolution.xy;st.x *= u_resolution.x/u_resolution.y;vec3 color = vec3(0.0);float d = 0.0;// Remap the space to -1. to 1.//坐标系移到中心并把它映射到-1到1之间st = st *2.-1.;// Make the distance fieldd = length( abs(st)-.3 );// d = length( min(abs(st)-.3,0.) );// d = length( max(abs(st)-.3,0.) );// Visualize the distance fieldgl_FragColor = vec4(vec3(fract(d*10.0)),1.0);// Drawing with the distance field// gl_FragColor = vec4(vec3( step(.3,d) ),1.0);// gl_FragColor = vec4(vec3( step(.3,d) * step(d,.4)),1.0);// gl_FragColor = vec4(vec3( smoothstep(.3,.4,d)* smoothstep(.6,.5,d)) ,1.0);
}

我们一开始把坐标系移到中心并把它映射到-1到1之间(st = st *2.0-1.0)。

上面的d = length( abs(st)-.3 );的距离场方程。这里我们在计算点 (.3,.3) 或 vec3(.3)到所有四象限的距离(这就是 abs() 在起作用)。

然后我们用一个fract(x) 函数来(返回x的小数部分),呈现这个距离场产生的图案。这个距离场不断重复,就像在禅花园看到的环一样。让我们记住 fract() 函数。它返回一个数的分数部分,本质上是除1的余数(mod(x,1.0))。那么当frace(d*N)。当我们的N=1的时候,就只有一个0-d的距离场,当N=2,3,4.那我们就有N个0-d的距离场。

如果取消下一句的注释,你会发现我们把到四个点的距离用min() 函数合并到0,并产生了一个有趣的图案。

现在再试着取消下一句的注释,我们做的和之前一样,只不过这次用的是 max() 函数。这次的记过是圆角矩形。注意距离场的环形是如何离中心越远越光滑的。

最后一行行地取消最后几句距离场方程的注释,思考距离场的不同用途。

极坐标下的图形

在关于颜色的章节我们通过如下的方程把每个像素的 半径 和 角度 笛卡尔坐标映射到极坐标。

vec2 pos = vec2(0.5)-st;float r = length(pos)*2.0;float a = atan(pos.y,pos.x);

我们用了部分方程在这章的开头来画圆,即用 length() 计算到中心的距离。现在我们可以用极坐标来画圆。

极坐标这种方式虽然有所限制但却十分简单。

下面你会看到在同样在笛卡尔坐标下图形在极坐标下的着色器案例。对这些函数一个个取消注释,看看两坐标系之间的联系。

y = cos(x*3.);
//y = abs(cos(x*3.));
//y = abs(cos(x*2.5))*0.5+0.3;
//y = abs(cos(x*12.)*sin(x*3.))*.8+.1;
//y = smoothstep(-.5,1., cos(x*10.))*0.2+0.5;
// Author @patriciogv - 2015
// http://patriciogonzalezvivo.com#ifdef GL_ES
precision mediump float;
#endifuniform vec2 u_resolution;
uniform vec2 u_mouse;
uniform float u_time;void main(){vec2 st = gl_FragCoord.xy/u_resolution.xy;vec3 color = vec3(0.0);vec2 pos = vec2(0.5)-st;float r = length(pos)*2.0;float a = atan(pos.y,pos.x);float f = cos(a*3.);// f = abs(cos(a*3.));// f = abs(cos(a*2.5))*.5+.3;// f = abs(cos(a*12.)*sin(a*3.))*.8+.1;// f = smoothstep(-.5,1., cos(a*10.))*0.2+0.5;color = vec3( 1.-smoothstep(f,f+0.02,r) );gl_FragColor = vec4(color, 1.0);
}

玫瑰线

代码中有个极坐标的玫瑰线,是数学曲线中非常注明的曲线,看上去像花瓣,它只能用极坐标来描述:方程如下:
F(angle)=ACos(Kangle);

如果K是整数,当K是奇数时那么曲线将会是K个花瓣,当K是偶数将是2K个花瓣。如果K为非整数,将产生圆盘状圆形,且花瓣数也为非整数。变量A代表玫瑰线花瓣的长度

试着:

  • 让这些图形动起来。
  • 结合不同的造型函数来 雕刻 图形,制作诸如花,雪花和齿轮。
  • 用我们在 造型函数 章节的 plot() 函数画等高线。

整合的魅力

到目前为止,我们知道如何用atan()函数来根据角度调整半径以获得不同的图形,以及如何用atan()结合所以和距离场有关的技巧得到可能的效果。

看下下面来自Andrew Baldwin的例子。这里的技巧是用极坐标的方式通过定义多边形的边数来构建一个距离场。

#ifdef GL_ES
precision mediump float;
#endif#define PI 3.14159265359
#define TWO_PI 6.28318530718uniform vec2 u_resolution;
uniform vec2 u_mouse;
uniform float u_time;// Reference to
// http://thndl.com/square-shaped-shaders.htmlvoid main(){vec2 st = gl_FragCoord.xy/u_resolution.xy;st.x *= u_resolution.x/u_resolution.y;vec3 color = vec3(0.0);float d = 0.0;// Remap the space to -1. to 1.st = st *2.-1.;// Number of sides of your shapeint N = 3;// Angle and radius from the current pixelfloat a = atan(st.x,st.y)+PI;float r = TWO_PI/float(N);// Shaping function that modulate the distanced = cos(floor(.5+a/r)*r-a)*length(st);color = vec3(1.0-smoothstep(.4,.41,d));// color = vec3(d);gl_FragColor = vec4(color,1.0);
}

下面我尝试着解释这多边形的例子

  • 用这个例子,改造一个输入位置,指定图形(形状)的顶点数来返回一个距离场(的值)。
  • 结合使用 min() 和 max() 函数混合距离场。
  • 用距离场画个自己感兴趣的logo。

恭喜!你完成了最艰难的部分!休息下让这些概念沉淀一下吧 —— 用Processing 来画简单的形状很容易,但却不到火候。在 shader 的世界里,画形状是很纠结,而且适应这种新的编程范式会有些累人。

完整源码

// Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.  CCEffect %{techniques:- passes:- vert: vsfrag: fsblendState:targets:- blend: truerasterizerState:cullMode: noneproperties:texture: { value: white }alphaThreshold: { value: 0.5 }val1: {value: 0.4 }val2: {value: 0.6 }selectDistance: {value: 1.0 }shapeType: {value: 1.0}
}%CCProgram vs %{precision highp float;#include <cc-global>#include <cc-local>in vec3 a_position;in vec4 a_color;out vec4 v_color;#if USE_TEXTUREin vec2 a_uv0;out vec2 v_uv0;#endifvoid main () {vec4 pos = vec4(a_position, 1);#if CC_USE_MODELpos = cc_matViewProj * cc_matWorld * pos;#elsepos = cc_matViewProj * pos;#endif#if USE_TEXTUREv_uv0 = a_uv0;#endifv_color = a_color;gl_Position = pos;}
}%CCProgram fs %{precision highp float;#include <alpha-test>#include <texture>#include <cc-global>#include <cc-local>in vec4 v_color;#if USE_TEXTUREin vec2 v_uv0;uniform sampler2D texture;#endifuniform controllVal{float val1;float val2;float selectDistance;float shapeType;};float PI=3.1415;float NUM_E=2.7182;mat2 rotate2d(float angle){return mat2(cos(angle),-sin(angle),sin(angle),cos(angle));}float circle(in vec2 _st, in float _radius){vec2 dist = _st-vec2(0.5);float v1=dist.x*dist.x+dist.y*dist.y;return 1.-smoothstep(_radius-(_radius*0.01),_radius+(_radius*0.01),v1*4.0);}float distanceEffect(vec2 st){float pct=0.0;if(selectDistance==1.0) pct = distance(st,vec2(val1)) + distance(st,vec2(val2));if(selectDistance==2.0) pct = distance(st,vec2(val1)) * distance(st,vec2(val2));if(selectDistance==3.0) pct = min(distance(st,vec2(val1)),distance(st,vec2(val2)));if(selectDistance==4.0) pct = max(distance(st,vec2(val1)),distance(st,vec2(val2)));if(selectDistance==5.0) pct = pow(distance(st,vec2(val1)),distance(st,vec2(val2)));return pct;}vec3 zenGarden(vec2 st){vec3 _color=vec3(1.);float d=0.0;st = st *2.-1.;if(selectDistance==1.0) d = length( abs(st)-.3 );if(selectDistance==2.0) d = length( min(abs(st)-.3,0.) );if(selectDistance==3.0) d = length( max(abs(st)-.3,0.) );_color = vec3(fract(d*10.0));return _color;}vec3 polarCoordinates(vec2 st){/*   st-=vec2(.5);st=rotate2d(sin(cc_time.x)*PI)*st;st+=vec2(.5); */vec3 _color=vec3(1.);vec2 pos = vec2(0.5)-st;float r = length(pos)*2.0;float a = atan(pos.y,pos.x);float f=0.;if(selectDistance==1.0) f = cos(a*3.);if(selectDistance==2.0) f = abs(cos(a*3.));if(selectDistance==3.0) f = abs(cos(a*2.5))*.5+.3;if(selectDistance==4.0) f = abs(cos(a*12.)*sin(a*3.))*.8+.1;if(selectDistance==5.0) f = smoothstep(-.5,1., cos(a*10.))*0.2+0.5;if(selectDistance==6.0) f=1./(1.-0.005*cos(a));_color = vec3( 1.-smoothstep(f,f+0.02,r) );return _color;}vec3 polygonDistance(vec2 st){vec3 color = vec3(0.0);float d = 0.0;// Remap the space to -1. to 1.st = st *2.-1.;int n=3;if(selectDistance<=3.0) {n=3;}else{n=int(selectDistance);}// Number of sides of your shapeint N = int(n);// Angle and radius from the current pixelfloat a = atan(st.x,st.y)+PI;float r = (2.*PI)/float(N);// Shaping function that modulate the distanced = cos(floor(.5+a/r)*r-a)*length(st);color = vec3(1.0-smoothstep(.4,.41,d));return color;}void main () {vec4 o = vec4(1, 1, 1, 1);#if USE_TEXTURECCTexture(texture, v_uv0, o);#endifo *= v_color;vec3 _color=vec3(1.0);float _alpha=1.0;float pct = 0.0;// a. The DISTANCE from the pixel to the center// pct = distance(v_uv0,vec2(0.5));pct = distanceEffect(v_uv0);vec3 color=vec3(1.0);if(shapeType==1.0) color = vec3(distanceEffect(v_uv0));if(shapeType==2.0) color=polarCoordinates(v_uv0);if(shapeType==3.0) color=zenGarden(v_uv0);if(shapeType==4.0) color=polygonDistance(v_uv0); ALPHA_TEST(o);o=vec4(color,1.);gl_FragColor = o;}
}%

cocos cretor shader effect-the book of shader-3.形状相关推荐

  1. 【转】Silverlight 3 Beta 新特性解析(7)- Child Window和Shader Effect篇

    前提条件: 阅读本文之前请确认你已经安装了如下软件 Visual Studio 2008 (Express) SP1 Silverlight 3 Tools For Visual Studio Mic ...

  2. cocos cretor shader effect-the book of shader前言

    前言 说来惭愧,半年前开始接触cocso creator shader的时候,看了官方教程,看了大神写的例子,搞懂了一些语法概念.可是当时看一遍有种似懂非懂的感觉,那种感觉就是听过很多道理,任然过不好 ...

  3. 【HLSL学习笔记】WPF Shader Effect Library算法解读之[DirectionalBlur]

    原文:[HLSL学习笔记]WPF Shader Effect Library算法解读之[DirectionalBlur] 方位模糊是一个按照指定角度循环位移并叠加纹理,最后平均颜色值并输出的一种特效. ...

  4. unity shader入门精要_Unity Shader 入门(二):shader 基础

    一.参考与说明(需要写在开始东西): 1.1 Unity Shader 入门精要学习 https://github.com/candycat1992/Unity_Shaders_Book/tree/u ...

  5. creator shader:从零开始,用shader画个彩虹

    creator shader:从零开始,用shader画个彩虹 从创建shader和材质开始 分别创建名为 rainbow的effect和material,创建一个场景,新建一张Sprite精灵,使用 ...

  6. 用 shader effect 实现雨滴落水效果!Cocos Creator 3D !

    最近逛论坛时,看到一位大佬在分享各种 shader 特效.基于其中的水波 shader ,白玉无冰写了一个玩水效果!文章底部获取完整代码!还可以试试水哦! 先一起看看效果- 点击任意位置,会在该位置生 ...

  7. cocos中如何让背景模糊_Cocos Creator Shader Effect 系列 - 8 - 高斯模糊

    本章为大家带来高斯模糊的实现 2d-sprite-gaussian-blur-v1 首先,来点简单的,比如:高斯模糊的英文名叫 Gaussian Blur. 关于高斯模糊的原理,在我学习过程中,下面两 ...

  8. 麒麟子Cocos Creator 3D研究笔记十一:实用Shader之单张纹理实现武器动态发光

    零.别看广告,看疗效 虽然标题叫武器发光,其实它还能实现魔法护盾.动态光柱等效果.别说话,看图! 阿子最近沉迷于学英语和写代码,可HIGH了. 一看时间,竟然已经十天没发稿了. 但这不能怪阿子,要怪就 ...

  9. cocos2dx 字体外发光_Cocos Creator Shader Effect 系列 - 6 - 内发光特效

    本章为大家带来内发光特效. 2d-sprite-glow-inner.gif 一.内发光原理 学习 Shader 过程中,偶然在网上看到一句的内发光原理,十分精辟受用: 采样周边像素alpha取平均值 ...

最新文章

  1. __cplusplus的用处
  2. 亚信产业互联网生态亮相2016南京软博会
  3. sublime text3注册激活及失效解决办法
  4. 设计模式(八)代理模式(结构型)
  5. Python 将视频 截取 成图片 附cv2处理教程
  6. 【C/C++多线程编程之三】创建pthread线程
  7. spring配置xml文件_XML配置文件中的Spring配置文件
  8. VS生成Cordova for Android应用之Gradle
  9. 字符串:你看的懂的KMP算法(带验证)
  10. TensorFlow——共享变量的使用方法
  11. servlet和JSP页面乱码问题
  12. 什么是计算机游戏技术,dlss技术是什么意思有什么用?目前支持dlss的游戏有哪些?...
  13. 对比jQuery和AngularJS的不同思维模式
  14. docker镜像制作、数据管理
  15. 【PLC】01-使用PLC控制步进电机带威纶通触摸屏
  16. 微信小程序实现文件上传
  17. 订餐系统(饿了某)java程序实现
  18. css伪元素实现箭头和关闭及环形实心圆点
  19. AD转换精度提高方法
  20. TMS320f28335实现步进电机的驱动程序及原理分析

热门文章

  1. 放置大石头的艺术:让你的效率翻倍
  2. 借鉴德国工业4.0推动中国制造业转型升级
  3. 三极管替换及常用开关三极管
  4. 韩峰传 (转自水木)
  5. 大学计算机网络技术及应用考题,大学计算机网络技术应用试题
  6. AtCoder-Quadruple
  7. python 形式参数_Python 函数的参数形式
  8. Java JMM详解
  9. 【板栗糖GIS】arcmap—怎么批量修改符号系统的线型,颜色等
  10. 安装node.js - v18遇到的一些问题