光照模型:用数学的方法模拟现实世界中的光照效果。
 
场景中模型身上的光反射到相机中的光线:
1.漫反射:产生明暗效果
2.高光反射:产生镜面反射,物体中有最亮且比较耀眼的一部分
3.自发光:
4.环境光:
 
漫反射
光线被粗糙的表面无规则的向各个方向反射的现象。
漫反射的计算公式(兰伯特光照模型)模拟漫反射
漫反射光 = 光照颜色和强度 * 漫反射颜色 * max(0, dot(法线方向,  光照方向))
光照颜色和强度:Unity引擎提供的,CGIncludes/UnityLightingCommon.cginc,需要引用
漫反射颜色:从属性面板获取的
法线方向:世界空间下的:需要把模型空间下的法线转换到世界空间下 
光照方向:从顶点指向光源的CGIncludes/UnityCG.cginc

逐顶点漫反射光照:是把光照计算放在顶点函数里,效果差(过渡区域),效率高

Shader "Lesson/VFVertDiffuse" {
Properties{
_DiffuseColor("漫反射颜色", Color) = (,,,)
}
SubShader{
Pass
{
//设置正确的光照渲染模式,前项渲染里的基础
Tags { "LightMode" = "ForwardBase" }
//定义Cg的开始和结束
CGPROGRAM
#pragma vertex vert//定义顶点函数名字
#pragma fragment frag//定义片元函数名字
#include "Lighting.cginc"//如果想要使用提供一些灯光变量和方法,需要引入文件 //漫反射光 = 光照颜色和强度 * 漫反射颜色 * max(0, dot(法线方向, 光照方向))
//漫反射颜色:从属性面板获取 _DiffuseColor
//光照颜色:系统提供的,_LightColor0
//法线方向:世界空间下的:需要把模型空间下的法线转换到世界空间下 worldNormal
fixed4 _DiffuseColor;//重定义属性变量 struct m2v
{
//需要获取顶点坐标和法线向量
float4 vex : POSITION;
float3 normal : NORMAL;
};
struct v2f
{
//把光照计算放在顶点函数中
//所以需要把计算之后的漫反射光Color传递到片元函数中
float4 position : SV_POSITION;
fixed4 color : COLOR0;
};
//实现定点和片元函数
v2f vert(m2v v)
{
v2f f;
f.position = UnityObjectToClipPos(v.vex);//转换顶点坐标
float3 worldNormal = normalize(mul(UNITY_MATRIX_M, v.normal));//normalize归一化向量
float3 worldPosition = mul(UNITY_MATRIX_M, v.vex);//先获取世界空间下的顶点坐标
float3 lightDir = normalize(UnityWorldSpaceLightDir(worldPosition));//UnityWorldSpaceLightDir,获取光照方向
fixed3 color = _LightColor0.xyz * _DiffuseColor.xyz * max(, dot(worldNormal, lightDir));//漫反射颜色,光照不影响透明度,所以是.xyz没有w
f.color = fixed4(color, );
return f;
}
fixed4 frag(v2f f) : SV_Target
{
return f.color;
}
ENDCG
}
}
FallBack "Diffuse"
}

逐像素漫反射光照:是把光照计算放在片元函数里,效果好,效率低

Shader "Lesson/VFFragDiffuse" {
Properties{
_DiffuseColor("漫反射颜色", Color) = (,,,)
}
SubShader{
Pass
{
Tags { "LightMode" = "ForwardBase" }
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
fixed4 _DiffuseColor; struct m2v
{
float4 vex : POSITION;
float3 normal : NORMAL;
};
struct v2f
{
float4 position : SV_POSITION;
float3 worldNormal : COLOR0;//把世界空间下的法线传递过去
float3 worldPosition : COLOR1;//世界空间下的顶点坐标
};
v2f vert(m2v v)
{
v2f f;
f.position = UnityObjectToClipPos(v.vex);
f.worldNormal = normalize(mul(UNITY_MATRIX_M, v.normal));//转换法线向量,变成世界空间下的
f.worldPosition = mul(UNITY_MATRIX_M, v.vex);
return f;
}
fixed4 frag(v2f f) : SV_Target
{
float3 lightDir = normalize(UnityWorldSpaceLightDir(f.worldPosition));
fixed3 color = _LightColor0.xyz * _DiffuseColor.xyz * max(, dot(f.worldNormal, lightDir));
return fixed4(color, );
}
ENDCG
}
}
FallBack "Diffuse"
}
Pass
{
//设置正确的光照渲染模式,前项渲染里的基础
Tags { "LightMode" = "ForwardBase" }
}

半兰伯特光照模型,解决兰伯特背光面毫无细节的问题。
漫反射光 = 光照颜色和强度 * 漫反射颜色 * (dot(法线方向,  光照方向) * 0.5 + 0.5)
 
实现半兰伯特光照的逐像素渲染和逐顶点渲染
fixed3 color = _LightColor0.xyz * _DiffuseColor.xyz * (dot(f.worldNormal, lightDir) * 0.5 + 0.5);
 
高光反射
高光:物体上最亮的部分。
镜面反射:当平行的入射光线射到物体光滑的表面,仍会平行的向一个方向反射出来。
高光(Specular) = 光照颜色和强度 * 高光颜色 * pow(max(0, dot(v, r)), 高光系数)
V:顶点到相机的方向
R:反射光的方向
Shader "Lesson/VFVertSpecular" {
Properties{
_DiffuseColor("漫反射颜色", Color) = (,,,)
_SpecularColor("高光颜色", Color) = (,,,)
_Gloss("高光系数", Range(, )) = 0.2
}
SubShader{
Pass
{
Tags { "LightMode" = "ForwardBase" }
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc" fixed4 _DiffuseColor;
fixed4 _SpecularColor;
float _Gloss; struct m2v
{
float4 vex : POSITION;
float3 normal : NORMAL;
};
struct v2f
{
float4 position : SV_POSITION;
fixed4 color : COLOR0;
};
v2f vert(m2v v)
{
v2f f;
f.position = UnityObjectToClipPos(v.vex);
//高光(Specular) = 光照颜色和强度 * 高光颜色 * pow(max(0, dot(v, r)), 高光系数)
//V:顶点到相机的方向 UnityWorldSpaceViewDir(worldPosition)
//R:反射光的方向 reflect(-lightDir, worldNormal)
//光照颜色和强度 _LightColor0
//高光颜色 _SpecularColor
//高光系数 _Gloss
//世界空间下的法线
float3 worldNormal = normalize(mul(UNITY_MATRIX_M, v.normal));
//世界空间下的顶点坐标
float3 worldPosition = mul(UNITY_MATRIX_M, v.vex);
//顶点到光源的方向
float3 lightDir = normalize(UnityWorldSpaceLightDir(worldPosition));
//顶点到相机的方向
float3 cameraDir = normalize(UnityWorldSpaceViewDir(worldPosition));
//反射光的方向
float3 refDir = reflect(-lightDir, worldNormal);
//漫反射 半兰伯特
fixed3 diffuse = _LightColor0.xyz * _DiffuseColor.xyz * (dot(worldNormal, lightDir) * 0.5 + 0.5);
//高光
fixed3 specular = _LightColor0.xyz * _SpecularColor.xyz * pow(max(, dot(cameraDir, refDir)), _Gloss);
f.color = fixed4(diffuse + specular, );
return f;
}
fixed4 frag(v2f f) : SV_Target
{
return f.color;
}
ENDCG
}
}
FallBack "Diffuse"
}

实现漫反射,高光,自发光、贴图纹理混合影响的Shader

// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'
Shader "Lesson/VFFragSpecular" {
Properties{
_MainTex("纹理", 2D) = "white"{}
_DiffuseColor("漫反射颜色", Color) = (,,,)
_SpecularColor("高光颜色", Color) = (,,,)
_Emission("自发光颜色", Color) = (0.05,0.05,0.05,)
_Gloss("高光系数", Range(, )) = 1.05
}
SubShader{
Pass
{
Tags{ "LightMode" = "ForwardBase" }
CGPROGRAM #pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc" //重定义
fixed4 _DiffuseColor;
fixed4 _SpecularColor;
fixed4 _Emission;
float _Gloss;
sampler2D _MainTex; struct m2v
{
float4 vex : POSITION;
float3 normal : NORMAL;
float2 uv : TEXCOORD0;
}; struct v2f
{
//转换之后的坐标
float4 position : SV_POSITION;
//世界空间下法线向量
float3 worldNormal : COLOR0;
//世界空间下的顶点坐标
float3 worldPosition : COLOR1;
//uv
float2 uv : TEXCOORD0;
};
v2f vert(m2v v)
{
v2f f;
f.position = UnityObjectToClipPos(v.vex);
f.worldNormal = normalize(mul(UNITY_MATRIX_M, v.normal));
f.worldPosition = mul(UNITY_MATRIX_M, v.vex);
f.uv = v.uv;
return f;
} fixed4 frag(v2f f) : SV_Target
{
/*高光(Specular) = 光照颜色和强度 * 高光颜色 * pow(max(0, dot(v, r)), 高光系数)
V: 顶点到相机的方向 UnityWorldSpaceViewDir(世界空间下的顶点坐标)
R:反射光的方向 reflect(从光源指向顶点的方向, 世界空间下的法线方向)
*/ //顶点到相机的方法
float3 cameraDir = normalize(UnityWorldSpaceViewDir(f.worldPosition));
//光照方向 从顶点指向光源方法
float3 lightDir = normalize(UnityWorldSpaceLightDir(f.worldPosition));
//反射光方向 -lightDir 从光源指向顶点方法
float3 refDir = reflect(-lightDir, f.worldNormal);
//漫反射 半兰伯特
fixed3 diffuse = _LightColor0.xyz * _DiffuseColor.xyz * (dot(f.worldNormal,lightDir) * 0.5 + 0.5);
//高光
fixed3 specular = _LightColor0.xyz * _SpecularColor.xyz * pow(max(, dot(cameraDir, refDir)), _Gloss);
//自发光 _Emission
//环境光
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
//光的颜色
fixed3 lightColor = diffuse + specular + _Emission + ambient;
//根据UV获取纹理的颜色
fixed4 texColor = tex2D(_MainTex, f.uv);
texColor.xyz = texColor.xyz * lightColor;
return texColor;
}
ENDCG
}
}
FallBack "Diffuse"
}

多光情况

// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'
Shader "Lesson/VFMoreLightDiffuse" {
Properties{
_DiffuseColor("漫反射颜色", Color) = (,,,)
}
SubShader{
Pass
{
Tags{ "LightMode" = "ForwardBase" }
CGPROGRAM #pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc" //重定义
fixed4 _DiffuseColor; struct m2v
{
float4 vex : POSITION;
float3 normal: NORMAL;
}; struct v2f
{
float4 position : SV_POSITION;
fixed4 color : COLOR0;
}; v2f vert(m2v v)
{
v2f f;
f.position = UnityObjectToClipPos(v.vex); //世界空间下的法线
float3 worldNormal = normalize(mul(UNITY_MATRIX_M, v.normal));
//光照方向
float3 lightDir = normalize(UnityWorldSpaceLightDir(mul(UNITY_MATRIX_M, v.vex))); fixed3 diffuse = _LightColor0.xyz * _DiffuseColor.xyz * max(, dot(worldNormal, lightDir)); f.color = fixed4(diffuse, );
return f;
} fixed4 frag(v2f f) : SV_Target
{
return f.color;
}
ENDCG
}
Pass
{
Blend One One//把点光源(新)和太阳光(旧)混合
Tags{ "LightMode" = "ForwardAdd" }//最多添加4盏光源
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc" //重定义
fixed4 _DiffuseColor;
struct m2v
{
float4 vex : POSITION;
float3 normal: NORMAL;
};
struct v2f
{
float4 position : SV_POSITION;
fixed4 color : COLOR0;
};
v2f vert(m2v v)
{
v2f f;
f.position = UnityObjectToClipPos(v.vex);
//世界空间下的法线
float3 worldNormal = normalize(mul(UNITY_MATRIX_M, v.normal));
//光照方向
float3 lightDir = normalize(UnityWorldSpaceLightDir(mul(UNITY_MATRIX_M, v.vex)));
fixed3 diffuse = _LightColor0.xyz * _DiffuseColor.xyz * max(, dot(worldNormal, lightDir));
f.color = fixed4(diffuse, );
return f;
}
fixed4 frag(v2f f) : SV_Target
{
return f.color;
}
ENDCG
}
}
FallBack "Diffuse"
}

补充内容

补充:在障碍物后面显示轮廓阴影

Shader "Lesson/PlayerBody" {
Properties {
_Color ("Color", Color) = (,,,)
_OcculusionColor ("被挡住以后的颜色", Color) = (,,,)
}
SubShader {
Tags { "Queue"="Geometry+200" }
Pass
{
ZTest Greater
Color[_OcculusionColor]
}
Pass
{
ZTest Less
Color[_Color]
}
}
FallBack "Diffuse"
}

补充:物体多个材质球渲染,unity mobile shader:解决眼镜和身体的遮挡关系

Shader "Lesson/Glass" {
Properties {
_Color ("Color", Color) = (,,,)
_OcculusionColor ("被挡住以后的颜色", Color) = (,,,)
}
SubShader {
Tags { "Queue"="Geometry+100" }
Pass
{
ZTest Greater
ZWrite off
Color[_OcculusionColor]
}
Pass
{
ZTest Less
Color[_Color]
}
}
FallBack "Diffuse"
}

边缘发光

// Upgrade NOTE: replaced '_World2Object' with 'unity_WorldToObject'
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'
Shader"Lesson/OutLine1"
{
Properties
{
_MainTex("main tex",2D) = "black"{}
_RimColor("rim color",Color) = (,,,)//边缘颜色
_RimPower("rim power",range(,)) = //边缘强度
}
SubShader
{
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include"UnityCG.cginc"
struct v2f
{
float4 vertex:POSITION;
float4 uv:TEXCOORD0;
float4 NdotV:COLOR;
};
sampler2D _MainTex;
float4 _RimColor;
float _RimPower;
v2f vert(appdata_base v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = v.texcoord;
float3 V = WorldSpaceViewDir(v.vertex);
V = mul(unity_WorldToObject,V);//视方向从世界到模型坐标系的转换
o.NdotV.x = saturate(dot(v.normal,normalize(V)));//必须在同一坐标系才能正确做点乘运算
return o;
}
half4 frag(v2f IN) :COLOR
{
half4 c = tex2D(_MainTex,IN.uv);
//用视方向和法线方向做点乘,越边缘的地方,法线和视方向越接近90度,点乘越接近0.
//用(1- 上面点乘的结果)*颜色,来反映边缘颜色情况
c.rgb += pow(( - IN.NdotV.x) ,_RimPower)* _RimColor.rgb;
return c;
}
ENDCG
}
}
FallBack"Diffuse"
}
 
轮廓
思路:两次渲染,第一次渲染背面,剔除正面,把模型顶点沿法线方向扩伸一定距离(用来表现描边的粗细)
第二次渲染,渲染正面,剔除背面
Shader "Lesson/OutLine2"
{
Properties
{
_MainTex("main tex",2D) = ""{}
_Factor("factor",Range(,0.1)) = 0.01//描边粗细因子
_OutLineColor("outline color",Color) = (,,,)//描边颜色
}
SubShader
{
Pass
{
Cull Front //剔除前面
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc" struct v2f
{
float4 vertex :POSITION;
}; float _Factor;
half4 _OutLineColor; v2f vert(appdata_full v)
{
v2f o;
//v.vertex.xyz += v.normal * _Factor;
//o.vertex = mul(UNITY_MATRIX_MVP,v.vertex); //变换到视坐标空间下,再对顶点沿法线方向进行扩展
float4 view_vertex = mul(UNITY_MATRIX_MV,v.vertex);
float3 view_normal = mul(UNITY_MATRIX_IT_MV,v.normal);
view_vertex.xyz += normalize(view_normal) * _Factor; //记得normalize
o.vertex = mul(UNITY_MATRIX_P,view_vertex);
return o;
} half4 frag(v2f IN) :COLOR
{
//return half4(0,0,0,1);
return _OutLineColor;
}
ENDCG
}
Pass
{
Cull Back //剔除后面
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc" struct v2f
{
float4 vertex :POSITION;
float4 uv:TEXCOORD0;
}; sampler2D _MainTex; v2f vert(appdata_full v)
{
v2f o;
o.vertex = mul(UNITY_MATRIX_MVP,v.vertex);
o.uv = v.texcoord;
return o;
} half4 frag(v2f IN) :COLOR
{
//return half4(1,1,1,1);
half4 c = tex2D(_MainTex,IN.uv);
return c;
}
ENDCG
}
}
FallBack "Diffuse"
}
 
描边
思路:两次渲染。第一次渲染背面,剔除正面。利用Offset指令,向离摄像机更近的方式偏移
第二次正常渲染物体(不剔除),(或者可以渲染正面,剔除背面)。
Offset:深度偏移
Offset Factor,Units
Factor参数表示 Z缩放的最大斜率的值。
Units参数表示可分辨的最小深度缓冲区的值。
我的理解:深度偏移,Unity是左手坐标系,Z的正方向是朝屏幕里面。
沿Z的负方向偏移就是离摄像机更近,
沿Z的正方向偏移就是离摄像机更远。
作用:可以强制使位于同一位置上的两个几合体中的一个几何体绘制在另一个的上层
比如:几何体A的Z值比几何体B更远,此时B是在A的上层。
但是,给A设置了Offset 往Z的负方向偏移,此时A比B更近了,A就在B上层了。
在深度测试(Z test)的时候,会去比较Z值,近的会绘制在远的上面。
所以上面的思路,要么第一次渲染背面,往Z的负方向偏移
要么第二次渲染的,往Z的正方向偏移,都是可以的。
Shader "Lesson/OutLine3"
{
Properties
{
_MainTex("main tex", 2D) = ""{ }
_OutLineColor("outline color",Color) = (,,,)//描边颜色
}
SubShader
{
//描边
pass
{
Cull Front
Offset -,- //深度偏移
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc" sampler2D _MainTex;
half4 _OutLineColor; struct v2f
{
float4 pos : SV_POSITION;
}; v2f vert(appdata_base v)
{
v2f o;
o.pos = mul(UNITY_MATRIX_MVP,v.vertex);
return o;
} float4 frag(v2f i) : COLOR
{
return _OutLineColor;
}
ENDCG
}
//正常渲染物体
pass
{
//Cull Back
//Offset 5,-1
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc" sampler2D _MainTex;
float4 _MainTex_ST; struct v2f
{
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
}; v2f vert(appdata_base v)
{
v2f o;
o.pos = mul(UNITY_MATRIX_MVP,v.vertex);
o.uv = TRANSFORM_TEX(v.texcoord,_MainTex);
return o;
} float4 frag(v2f i) : COLOR
{
float4 c = tex2D(_MainTex,i.uv);
return c;
}
ENDCG
}
}
}

Unity3D学习笔记(三十六):Shader着色器(3)- 光照的更多相关文章

  1. Unity3D学习笔记(十六):Animator新动画

    新动画系统: 给模型选择动画类型 普通动画:Generic 人形动画:Humanoid 建立动画控制器 - 在Project右击 - 选择Create-AnimatorContorller 将对应动画 ...

  2. 【Unity 3D】学习笔记三十六:物理引擎——刚体

    物理引擎就是游戏中模拟真是的物理效果.如两个物体发生碰撞,物体自由落体等.在unity中使用的是NVIDIA的physX,它渲染的游戏画面很逼真. 刚体 刚体是一个很很中要的组件. 默认情况下,新创的 ...

  3. PHP学习笔记三十六【try 二】

    <?php //定义一个顶级异常处理器 要定义在最上面 function my_exception($e) { echo "我是顶级异常处理:".$e->getMess ...

  4. Nodejs学习笔记(十六)--- Pomelo介绍&入门

    目录 前言&介绍 安装Pomelo 创建项目并启动 创建项目 项目结构说明 启动 测试连接 聊天服务器 新建gate和chat服务器 配置master.json 配置servers.json ...

  5. Nodejs学习笔记(十六)—Pomelo介绍&入门

    前言&介绍 Pomelo:一个快速.可扩展.Node.js分布式游戏服务器框架 从三四年前接触Node.js开始就接触到了Pomelo,从Pomelo最初的版本到现在,总的来说网易出品还算不错 ...

  6. angular学习笔记(三十)-指令(10)-require和controller

    本篇介绍指令的最后两个属性,require和controller 当一个指令需要和父元素指令进行通信的时候,它们就会用到这两个属性,什么意思还是要看栗子: html: <outer‐direct ...

  7. angular学习笔记(三十)-指令(7)-compile和link(2)

    继续上一篇:angular学习笔记(三十)-指令(7)-compile和link(1) 上一篇讲了compile函数的基本概念,接下来详细讲解compile和link的执行顺序. 看一段三个指令嵌套的 ...

  8. angular学习笔记(三十)-指令(7)-compile和link(1)

    这篇主要讲解指令中的compile,以及它和link的微妙的关系. link函数在之前已经讲过了,而compile函数,它和link函数是不能共存的,如果定义了compile属性又定义link属性,那 ...

  9. angular学习笔记(三十)-指令(6)-transclude()方法(又称linker()方法)-模拟ng-repeat指令

    在angular学习笔记(三十)-指令(4)-transclude文章的末尾提到了,如果在指令中需要反复使用被嵌套的那一坨,需要使用transclude()方法. 在angular学习笔记(三十)-指 ...

  10. angular学习笔记(三十)-指令(5)-link

    这篇主要介绍angular指令中的link属性: link:function(scope,iEle,iAttrs,ctrl,linker){ .... } link属性值为一个函数,这个函数有五个参数 ...

随机推荐

  1. git 的版本回滚

    当用git clone 复制远程代码库到本地时,使用 git branch 只能看到默认库(master),当远程库有多个分支时,可以使用 git branch -a 查看全部的分支, 然后git c ...

  2. Code First 指定外键名称

    指定类外键有注释(DataAnnotation)和FluentAPI两种方式, 目前掌握的知识我们先把DataAnnotation可用的四种方式总结如下 第一种方法: //1-指定导航属性,会自动生成 ...

  3. ThinkPHP 中使用 PHPMailer 发送邮件 支持163和QQ邮箱等

    [摘要]ThinkPHP是一个开源的PHP框架, 是为了简化企业级应用开发和敏捷WEB应用开发而诞生的.本文介绍ThinkPHP 中使用 PHPMailer 发送邮件. PHP是自带可以发送邮件的Ma ...

  4. mysql待整理

    1. MYSQL SQL_NO_CACHE的真正含义 http://www.dewen.org/q/5149/Mysql 是 结果不缓存,但查询还是缓存了. 如果要重新测试,就在查询前先执行一下&qu ...

  5. [Swift]LeetCode891. 子序列宽度之和 | Sum of Subsequence Widths

    Given an array of integers A, consider all non-empty subsequences of A. For any sequence S, let the  ...

  6. redux源码解读(二)

    之前,已经写过一篇redux源码解读(一),主要分析了 redux 的核心思想,并用100多行代码实现一个简单的 redux .但是,那个实现还不具备合并 reducer 和添加 middleware ...

  7. 图像中的artifacts

    artifacts 瑕疵 伪影(Artifacts) 伪影(Artifacts)-CT-基础术语 - 影像园 http://www.xctmr.com/baike/ct/c34b5413e305b45 ...

  8. Delphi - 如何执行Windows、OSX、Linux的外部程序?

    毫无疑问,几乎对所有Delphi程序员来说,不用说如何在Windows下如何执行外部程序了!目前Delphi,真的已经很棒了,Delphi有一套和VCL并驾齐驱的图形界面库,叫做"FireM ...

  9. 像素(PX)转其它长度单位(mm、cm...)

    今天想把px转成mm为单位,因像素跟其它单位比值的大小会跟屏幕设置的分辨率大小而不定,因此不能以固定的数值去计算. 解决方法是 页面上放一个高度为1mm的隐藏块 <div id="di ...

  10. August 07th 2017 Week 32nd Monday

    Life is just a series of trying to make up your mind. 生活只是由一系列下决心的努力所构成. Some people say it is not y ...