在上一篇中,我们基本上说明了遮挡描边实现的一种基本原理。这一篇中我们将了解一下基于这种原理的具体实现代码。本篇中的内容和前几篇教程相比,相对比较难一些,建议先有一些基本的Unity的C#脚本编程经验和基本的Unity Shader基础(可参考前几篇教程)。

  下面我们就开始讲解具体的实现代码(由于代码较多,所以这里只对需要讲解的地方进行讲解):


C#脚本部分


  与之前不同,这一次需要写一个C#脚本来辅助我们实现这个功能。它所做的主要工作就是创建一个临时摄像机用来获取我们想进行描边的物体的深度图,以及通过OnRenderImage函数对已经渲染完毕的主摄像机图像利用我们编写的Shader进行特殊处理,将处理完的结果再渲染到屏幕上。

 /*************
** author:esfog
/************/
using UnityEngine;
using System.Collections; public class PlayerOutLine : MonoBehaviour
{
//遮挡描边颜色
public Color OutLineColor = Color.green; private GameObject _cameraGameObject;
//摄像机(专门用来处理遮挡描边的)
private Camera _camera;
private Camera _mainCamera;
//所需的RenderTexture
private RenderTexture _renderTextureDepth;
private RenderTexture _renderTextureOcclusion;
private RenderTexture _renderTextureStretch; //临时材质
private Material _materialOcclusion;
private Material _materialStretch;
private Material _materialMix;
//用来处理玩家深度的Shader
private Shader _depthShader; // 相关初始化
void Start ()
{
_mainCamera = Camera.main;
_mainCamera.depthTextureMode = DepthTextureMode.Depth;
_depthShader = Shader.Find("Esfog/OutLine/Depth"); _cameraGameObject = new GameObject ();
_cameraGameObject.transform.parent = _mainCamera.transform;
_cameraGameObject.transform.localPosition = Vector3.zero;
_cameraGameObject.transform.localScale = Vector3.one;
_cameraGameObject.transform.localRotation = Quaternion.identity; _camera = _cameraGameObject.AddComponent<Camera> ();
_camera.aspect = _mainCamera.aspect;
_camera.fieldOfView = _mainCamera.fieldOfView;
_camera.orthographic = false;
_camera.nearClipPlane = _mainCamera.nearClipPlane;
_camera.farClipPlane = _mainCamera.farClipPlane;
_camera.rect = _mainCamera.rect;
_camera.depthTextureMode = DepthTextureMode.None;
_camera.cullingMask = << (int)LayerMask.NameToLayer ("Main Player");
_camera.enabled = false;
_materialOcclusion = new Material(Shader.Find("Esfog/OutLine/Occlusion"));
_materialStretch = new Material (Shader.Find ("Esfog/OutLine/Stretch"));
_materialMix = new Material (Shader.Find("Esfog/OutLine/Mix"));
Shader.SetGlobalColor ("_OutLineColor",OutLineColor);
if (!_depthShader.isSupported || !_materialMix.shader.isSupported || !_materialMix.shader.isSupported || !_materialOcclusion.shader.isSupported)
{
return;
}
} void OnRenderImage(RenderTexture source,RenderTexture destination)
{
_renderTextureDepth = RenderTexture.GetTemporary(Screen.width,Screen.height,,RenderTextureFormat.Depth);
_renderTextureOcclusion = RenderTexture.GetTemporary (Screen.width,Screen.height,);
_renderTextureStretch = RenderTexture.GetTemporary (Screen.width,Screen.height,);
_camera.targetTexture = _renderTextureDepth; _camera.fieldOfView = _mainCamera.fieldOfView;
_camera.aspect = _mainCamera.aspect;
_camera.RenderWithShader (_depthShader,string.Empty); //对比我们为角色生成的RenderTexture和主摄像机自身的深度缓冲区,计算出角色的哪些区域被挡住了
Graphics.Blit(_renderTextureDepth,_renderTextureOcclusion,_materialOcclusion);
var screenSize = new Vector4(1.0f/Screen.width,1.0f/Screen.height,0.0f,0.0f); _materialStretch.SetVector ("_ScreenSize",screenSize);
Graphics.Blit (_renderTextureOcclusion,_renderTextureStretch,_materialStretch,);
Graphics.Blit (_renderTextureStretch,_renderTextureStretch,_materialStretch,); _materialMix.SetTexture ("_OcclusionTex",_renderTextureOcclusion);
_materialMix.SetTexture ("_StretchTex",_renderTextureStretch);
Graphics.Blit (source,destination,_materialMix); RenderTexture.ReleaseTemporary (_renderTextureDepth);
RenderTexture.ReleaseTemporary (_renderTextureOcclusion);
RenderTexture.ReleaseTemporary (_renderTextureStretch); }
}

   10~26行,这部分主要是声明一些我们在后面将要使用到的变量,包括我们指定的描边颜色,对主摄像机的引用,以及对我们在后面要创建的临时摄像机的引用,3个RenderTexture,1个Shader,以及三个Material.对RenderTexture做一下解释:一般来说场景中的Camera渲染完毕后图像是直接显示在游戏屏幕上的,但是我们也可以创建一个RenderTexture,然后把相机的输出目标制定为这个RenderTexture上面,那么我们将不会在屏幕上看到这个相机的任何渲染结果,因为结果已经被保存到我们指定的RenderTexture了。你也可以把屏幕窗口理解为一个默认的RenderTexture.后面具体应用还会说明。

  31~33行,首先获得对场景主摄像机的引用,再通过_mainCamera.depthTextureMode = DepthTextureMode.Depth;将主摄像机的深度图模式设置为Depth.这样我们的主摄像机就会为我们提供深度图了,Shader中我们就可以直接使用_CameraDepthTexture来使用这张深度图了.然后我们通过Shader.Find()来初始化前面我们声明的一个Shader变量,后面我们会用到。

  35~50行,我们开始创建我们的专门用来生成被描边物体的深度图的摄像机了.我们把生成的摄像机作为主摄像机的子物体,主要是为了比较容易调整位置,没什么特殊意义.由于我们要保证这个临时摄像机和主摄像机拍摄角度和范围一模一样,我们把子物体的位置等信息都置零,然后把临时相机的参数全部设置成和主摄像机一样,这里的临时摄像机我们通过_camera.depthTextureMode = DepthTextureMode.None;让它不生成深度图,因为前面我们已经让主摄像机的深度模式为Depth了,而Shader中的_CameraDepthTexture只能用于一个主摄像机的深度图,所以所以这里面我们把临时相机的DepthTextureMode设置为None.然后我们后面通过我们自己编写的Shader来获得深度信息。最后这句_camera.cullingMask = 1 << (int)LayerMask.NameToLayer ("Main Player");是为了让我们的相机只渲染我们想要描边的物体,cullingMask属性是一个通过不同位上的01值来判断是否渲染该层的物体,这里我们创建一共新层叫"Main Player",并在场景中把我们的要渲染的物体的Layer设置为Main Player.这样就能通过这行代码就实现了整个目的。

  51~58行,前三行,我们初始化了前面声明的三个Material变量.可以看到Material的构造函数需要我们提供一个Shader,这里面我们通过Shader.Find()把我们编写的3个Shader用到三个Material上,Shader内容和用途在后面说明.Shader.SetGlobalColor ("_OutLineColor",OutLineColor);这句话就是一种通过C#脚本来给Shader中变量进行赋值的一种方法,但是这种方法是全局设置的,所以可能所有Shader的同名变量都会受到印象,所以使用前要确保只初始化了想要初始化的Shader变量。这句话就是把我们Shader中的描边颜色设置成我们OutLineColor表示的颜色.最后面的if语句,是用来判断一下是否我们编写的4个Shader是否能被支持.

  61行,OnRenderImage(RenderTexture source,RenderTexture destination),这个方法MonoBehaviour提供的,如果你在脚本中写上这个函数的话,那么如果你把这个脚本挂在含有Camera组件的物体时候,当这个摄像机渲染完的时候会调用这个OnRenderImage,把当前渲染结果当做第一个参数传入,也就是这里的source,然后通过我们对source的处理,最后把处理结果赋给destination,这个destination就是摄像机最终的渲染结果了。所以我们主要的处理步骤都是在OnRenderImage里面进行的.

  63~65行,我们前面声明了三个RenderTexture,这里就是初始化他的地方,其中_renderTextureDepth用来保存临时摄像机获取到的深度图信息,_renderTextureOcclusion用来保存我们进行Occlusion处理后的图像信息,这个处理就是通过Shader来找出需要描边物体的被遮挡部分._renderTextureStretch用来保存被遮挡区域拉伸后的信息,这几个操作都是按次序来个,前一个的输出作为后一个的输入.RenderTexture.GetTemporary()函数是Unity提供给我们初始化RenderTexture使用的,前两个参数是指定Texture的宽高,第三个参数是指定这个RenderTexture处理时候所涉及到的深度缓冲的具体精度(24是最大精度),第四个参数是指定图像保存的格式.这里面我们只有_renderTextureDepth比较特殊,由于他在处理过程中需要涉及到深度信息,所以第三个参数设置为24,最后一个参数设置为RenderTextureFormat.Depth是为了于Unity默认处理深度图的格式保持一致,以便我们后面处理。而其他两张RenderTexture都只是对二维图像进行处理,不需要深度信息,所以第三个参数可以设置为0.

  66~70行,_camera.targetTexture = _renderTextureDepth;=这一句把临时摄像机的渲染目标指定为为我们刚刚初始化的RenderTexture,前面已经解释过为什么这么做了.中间两句让临时摄像机的FOV和Aspect和主摄像机保持一致,因为有时候游戏中是可以拉近或者拉远视角的,而我们只有在Start方法里初始化了一次临时摄像机的参数。所以如果不修改的话可能导致两个摄像机参数不一致的问题。_camera.RenderWithShader这一句是为了让临时摄像机利用这个我们提供的Shader来进行渲染, 也就是把这个摄像机处理的所有顶点信息传进这个Shader,然后把Shader的返回结果作为输出保存到摄像机的targetTexture上.其中第一个参数是指定我们用到的Shader,第二个参数用不到,这里不解释了.

  73行,Graphics.Blit()是后期处理中的常用方法,他把第一个参数作为输入,利用第三个参数提供的材质所包含的Shader进行处理,处理后把结果输出到第二个参数上去.所以这一行中我们把上一步中得到的临时相机深度图作为输入,利用我们初始化的负责Occlusion处理的Material来获取被遮挡区域,再把结果保存到_renderTextureOcclusion上.

  74~78行,由于我们下面要对被遮挡区域进行分别进行一次横纵拉伸一像素的操作,而在Shader中我们只能对UV来处理,所以必须知道屏幕的一像素代表多少UV,第一行代码就是为了进行这个计算.第76行把计算好的值传到Shader中,这个方法和前面的全局给Shader赋值不同,这里只对相应材质对应的Shader进行了赋值.最后两行通过Blit分别进行了横纵的拉伸处理,最后面参数的0和1,是指调用Shader中的第几个Pass,其中0代表第一个.后面看具体Shader代码就明白了.

  80~82行,将前面两步处理的RenderTexture当做参数传递给最后一个Shader,然后我们通过Blit把主摄像机的原始图像当做输入,利用包含这个Shader的Material来处理,处理完成后把结果保存到destination上显示到游戏屏幕.

  84~86行,处理完毕要释放掉我们前面想系统申请的RenderTexture.

  下面分别来讲解我们用到的4个Shader:


获取深度信息的Shader


  第一个是用来获取临时相机深度图的.

 Shader "Esfog/OutLine/Depth"
{
SubShader
{
Tags { "RenderType"="Opaque" }
Pass
{
CGPROGRAM #pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc" struct v2f
{
float4 pos : SV_POSITION;
float2 depth : TEXCOORD0;
}; v2f vert (appdata_base v)
{
v2f o;
o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
o.depth = o.pos.zw;
return o;
} half4 frag(v2f i) : COLOR
{
half x= i.depth.x/i.depth.y;
return half4(x, x, x, x);
}
ENDCG
}
}
}

  就不逐行讲解了,主要注意一下下面几处:

   24行,这里我们用一个float2的depth变量来保存进行了MVP变换后的顶点的的z和w信息,这里要说明一下,透视投影要分为矩阵变换和,透视除法两个部分.UnityShader在顶点着色器处理完后只完成了矩阵变换,还没有进行透视除法,而o.pos.zw中利用z/w就能才能把顶点在投影空间的距离转换到0~1的空间.而后面的第31行正是进行了这样的处理。为什么返回half4(x,x,x,x).这可能是由于深度图的格式要求吧,这段代码在Unity官方文档上有.另外可以用UNITY_TRANSFER_DEPTH(o.depth);替换24行.用UNITY_OUTPUT_DEPTH(i.depth);来替换30~31行.这些在UnityCG.cginc上有定义,有兴趣可以看看.


检测遮挡区域的Shader


  第二个Shader是用来利用前面得到的深度图并结合出摄像机深度图来找出被遮挡区域的.

 Shader "Esfog/OutLine/Occlusion"
{
Properties
{
_MainTex ("Base (RGB)", 2D) = "white" {}
}
SubShader
{
Tags { "RenderType"="Opaque" } Pass
{
CGPROGRAM
#pragma vertex vert_img
#pragma fragment frag
#include "UnityCG.cginc" uniform sampler2D _MainTex;
uniform sampler2D _CameraDepthTexture;
uniform float4 _OutLineColor; float4 frag(v2f_img i):COLOR
{
float playerDepth = tex2D(_MainTex,i.uv);
float bufferDepth = tex2D(_CameraDepthTexture,i.uv);
float4 resColor = float4(,,,);
if((playerDepth < 1.0) && (playerDepth- bufferDepth)>0.0002)
{
resColor = float4(_OutLineColor.rgb,);
}
return resColor;
}
ENDCG
}
}
FallBack "Diffuse"
}

注意以下几处:

 14行,大家可能会奇怪为什么没有定义vertex函数,我们通过#pragma vertex vert_img使用了Unity为我们定义好的一些vertex函数,避免了自己手动编写,可以去UnityCG.cginc.看一下很简单。

 18~20行,注意这里的_MainTex,因为我们在外面是通过Graphics.Blit函数来使用这个Shader的,所以Blit函数的第一个RenderTexture参数会自动被赋给_MainTex,后面的Shader也都是如此.第二行的_CameraDepthTexture在前面我们已经说过了,是Unity为什么定义好的变量,这里面代表主摄像机的深度图.第三行的表面颜色,前面也已经在C#脚本里面赋值过了.

 24~30行,主摄像机的深度图和临时摄像机的深度图都是相对我们整个游戏窗口的两个二维图片,而游戏屏幕的当成一个大的模型,这个模型相当于一个大的面片只有4个顶点构成,uv就是0~1范围就对应于整个屏幕的宽高范围。所以前两行我们把两个深度图上相应uv上的像素颜色值取到,另外由于深度图的最终深度值都是存储在R通道上的,所以这里我们只用了两个float变量来接收tex2D的返回值.由于深度图中深度的信息范围是(0~1)其中0代表近平面,1代表远平面,值越大表示位置越靠后.if判断中playerDepth < 1.0是因为默认摄像机填充深度图的值就是1.0表示没有拍摄到物体,而palyerDepth<1.0就表示这个像素是在我们要被描边物体的身上的.而(playerDepth- bufferDepth)>0.0002是说明该像素所代表的位置在主摄像机上的深度比临时摄像机的深度要小,也就是这个像素实际上是被挡住的,这里的0.0002是因为避免相机深度的精度误差所引入的,不是必须的.如果if判断为true说明这个像素被遮挡,可以把颜色设置为描边颜色,否则使用本来的渲染颜色.


拉伸遮挡区域的Shader


第三个Shader是用来把上一步中得到的遮挡部分进行拉伸的.

 Shader "Esfog/OutLine/Stretch"
{
Properties
{
_MainTex ("Base (RGB)", 2D) = "white" {}
}
SubShader
{
Tags { "RenderType"="Opaque" }
Pass
{
CGPROGRAM
#pragma vertex vert_img
#pragma fragment frag
#include "UnityCG.cginc"
uniform sampler2D _MainTex;
uniform float4 _OutLineColor;
uniform float4 _ScreenSize; float4 frag(v2f_img i):COLOR
{
float4 c = tex2D(_MainTex,i.uv);
float4 c1 = tex2D(_MainTex,float2(i.uv.x-_ScreenSize.x,i.uv.y)); //左边一个像素
float4 c2 = tex2D(_MainTex,float2(i.uv.x+_ScreenSize.x,i.uv.y)); //右边一个像素
float3 totalCol = c.rgb + c1.rgb + c2.rgb;
float avg = totalCol.r + totalCol.g + totalCol.b;
if(avg > 0.01)
{
return _OutLineColor;
}
else
{
return float4(,,,);
}
} ENDCG
} Pass
{
Blend One One
CGPROGRAM
#pragma vertex vert_img
#pragma fragment frag
#include "UnityCG.cginc"
uniform sampler2D _MainTex;
uniform float4 _OutLineColor;
uniform float4 _ScreenSize; float4 frag(v2f_img i):COLOR
{
float4 c = tex2D(_MainTex,i.uv);
float4 c1 = tex2D(_MainTex,float2(i.uv.x,i.uv.y-_ScreenSize.y)); //下边一个像素
float4 c2 = tex2D(_MainTex,float2(i.uv.x,i.uv.y+_ScreenSize.y)); //上边一个像素
float3 totalCol = c.rgb + c1.rgb + c2.rgb;
float avg = totalCol.r + totalCol.g + totalCol.b;
if(avg > 0.01)
{
return _OutLineColor;
}
else
{
return float4(,,,);
}
} ENDCG
}
}
FallBack "Diffuse"
}

  这个Shader比较长是因为写了两个Pass,这两个Pass的内容很相近,第一个Pass用来横向拉伸一个像素,第二个Pass进行纵向拉伸一个像素.前面说到我们通过Blit函数的第四个参数来决定使用哪个Pass.注意以下几行:

  22~34行,在C#脚本中我们计算了游戏窗口的一个像素所代表的uv长度,所以我们这里以第一个Pass为例分别像当前像素的左边一个像素和右边一个像素取颜色,然后把他们的RGB值加在一起,如果大于0.01也就说明当前像素的左右区域是有颜色的,也就是属于描边区域,那么该像素就需要被设置为描边区域,设置成表面颜色,否则设置为黑.纵向拉伸也是同样的道理.


获取最终描边轮廓Shader


  最后一个Shader是利用我们上面处理取得的RenderTexture对主摄像机渲染的原始图像进行最终处理.

 Shader "Esfog/OutLine/Mix"
{
Properties
{
_MainTex ("Base (RGB)", 2D) = "white" {}
}
SubShader
{
Tags { "RenderType"="Opaque" }
Pass
{
CGPROGRAM
#pragma vertex vert_img
#pragma fragment frag
#include "UnityCG.cginc" uniform sampler2D _MainTex;
uniform sampler2D _OcclusionTex;
uniform sampler2D _StretchTex; float4 frag(v2f_img i):COLOR
{
float4 srcCol = tex2D(_MainTex,i.uv);
float4 occlusionCol = tex2D(_OcclusionTex,i.uv);
float4 stretchCol = tex2D(_StretchTex,i.uv);
float occlusionTotal = occlusionCol.r + occlusionCol.g + occlusionCol.b;
float stretchTotal = stretchCol.r + stretchCol.g + stretchCol.b; if(occlusionTotal <0.01f&&stretchTotal>0.01f)
{
return float4(stretchCol.rgb,srcCol.a);
}
else
{
return srcCol;
}
} ENDCG
}
}
FallBack "Diffuse"
}

  注意一下几行:

  17~19行,这三个变量均有C#脚本中进行了赋值,其中_MainTex代表主摄像机原始渲染结果,_OcclusionTex是表示包含了被遮挡部分信息的RenterTexture,_StretchTex表示被遮挡区域横纵拉伸一像素后的图像信息.

  23~27行,和上一个Shader类似,取出这三个Texture在当前uv所对应的颜色信息.并将后两者的rgb值各自进行累加.

  29~36行,在if判断中,如果当前像素的uv值在被遮挡区域_OcclusionTex中没有颜色(即occlusionTotal <0.01f),而在拉伸区域_StretchTex中有颜色值(即stretchTotal>0.01f)其实也就说明这个像素是在拉伸操作中被额外拉伸出来的那一像素.那么就把这个像素颜色设置为描边颜色,否则设置为图像正常渲染颜色。

  总算是写完了足足4个多小时,这篇下来确实比较长而且并没有逐行的解释说明,一方面我认为大家现在的水平已经不需要逐行解释了,而来也是觉得没必要事无巨细面面俱到,这样子反而写的很罗嗦,虽然现在这样子也挺啰嗦的.如果大家一次没有看明白,希望多看几遍,主要是理解C#脚本和Shader如何协作,以及多Shader的应用和后期处理的基本流程.这篇教程里面有几处地方我本人也并不是特别的清楚,所以只能是按照我自己的理解来进行了说明,希望大家在看的时候能够自己认真思考思考,我说的并不一定对,如果你发现了笔者哪里有错误,请在留言中指出。如果你希望通过下篇教程讲解哪方面的知识,也可以留言告诉我。希望大家有所收获。就写到这吧,我要下楼去转转了,坐的太久了.

  尊重他人智慧成果,欢迎转载,请注明作者esfog,原文地址http://www.cnblogs.com/Esfog/p/CoverOutline_Shader_Code.html 

Esfog_UnityShader教程_遮挡描边(实现篇)的更多相关文章

  1. Esfog_UnityShader教程_遮挡描边(原理篇)

    咳咳,有段时间没有更新了,最近有点懒!把不少精力都放在C++身上了.闲言少叙,今天要讲的可和之前的几篇有所不同了,这次是一个次综合应用.这篇内容中与之前不同主要体现在下面几点上. 1.之前我们写的都是 ...

  2. Esfog_UnityShader教程_镜面反射SpecularReflection

    系列教程第四篇,本来打算昨天写的,有些小偷懒就今天写了,这一期我们来讨论一下关于镜面反射的基本原理和具体代码.这一篇是承接着上一篇<Esfog_UnityShader教程_漫反射DiffuseR ...

  3. Esfog_UnityShader教程_前言

    很多人在学习Unity的时候对Shader都是一知半解,作为刚入职半年的新人接触Shader的时间也并不长,正因为是新人才能体会到学习Shader时候所遇到的困难和迷茫,无奈于资料不好找,网上难得的几 ...

  4. Esfog_UnityShader教程_漫反射DiffuseReflection

    这篇是系列教程的第三篇,最近工作比较紧,所以这个周六周日就自觉去加了刚回来就打开电脑补上这篇,这个系列的教程我会尽量至少保证一周写一篇的.如果大家看过我的上一篇教程<Esfog_UnitySha ...

  5. Esfog_UnityShader教程_逐帧动画

    有段日子没出这个系列的新文章了,今天就拿一个比较常见也比较基础的利用改变Shader来改变不断调整UV实现播放逐帧动画的小功能.很久没写了就当练练手了.在新版本的Unity中早就已经集成了Sprite ...

  6. Esfog_UnityShader教程_溶解效果Dissolve

    溶解效果在游戏中是很常见的,比如在一些神话或者魔法世界中,一些NPC角色在剧情需要时候会身体会渐渐的消失掉.甚至有一些更炫的,比如用火焰喷射器把目标燃尽.这些都可以用到溶解效果.这篇文章主要是讲解一下 ...

  7. Spring_MVC_教程_快速入门_深入分析

    Spring MVC 教程,快速入门,深入分析 博客分类: SPRING Spring MVC 教程快速入门  资源下载: Spring_MVC_教程_快速入门_深入分析V1.1.pdf Spring ...

  8. UnityShader实现物体被遮挡描边

    之前在网上看到物体遮挡描边的功能,自己也拿来实现了一番.算作第一篇博客的开篇. 先贴出几张效果图,也是个人思路和方案的改进路线吧. ////////////////////////////////// ...

  9. 【LaTeX】E喵的LaTeX新手入门教程(1)准备篇

    昨天熄灯了真是坑爹.前情回顾[LaTeX]E喵的LaTeX新手入门教程(1)准备篇 [LaTeX]E喵的LaTeX新手入门教程(2)基础排版上一期测试答案1.大家一开始想到的肯定是\LaTeX{}er ...

随机推荐

  1. 最小生成树——kruskal算法

    kruskal和prim都是解决最小生成树问题,都是选取最小边,但kruskal是通过对所有边按从小到大的顺序排过一次序之后,配合并查集实现的.我们取出一条边,判断如果它的始点和终点属于同一棵树,那么 ...

  2. 转:WebService通用接口

    看到许多中小项目的webservice接口的源代码,不禁有个吐槽的冲动.除了会用CXF,Axis2等神级框架,其他的懒得动了,都是Ctrl+V,Ctrl+C,把其他模块的Request,Respons ...

  3. 转换成的jar文件接收后台的信息乱码cmd解决办法

    运行——cmd——    java -jar -Dfile.encoding=utf-8 xxx.jar

  4. iOS 传值 委托(delegate)和block 对比

     技术交流新QQ群:414971585 这篇文章建议和前一篇一起看, 另外先弄清楚IOS的block是神马东东. 委托和block是IOS上实现回调的两种机制.Block基本可以代替委托的功能,而且实 ...

  5. 实验1_IPv6地址配置

    IPv6地址配置 实验任务 (1)掌握如何在路由器及PC上配置IPv6地址 (2)掌握如何用IPv6 ping命令进行IPv6地址可达性检查 (3)掌握如何用命令来查看IPv6地址配置 实验过程 在R ...

  6. python整理之(字符串、元组、列表、字典)

    一.关于字符串的整理总结 对于字符串的操作常用的有这些: 字符串的操作通过dir()函数可以查看 我们先整理没有下划线的用法,有下划线的暂时不去考虑. 1.capitalize 功能:使字符串的首字母 ...

  7. 用crontab跑定时任务[转]

    前一天学习了 at 命令是针对仅运行一次的任务,循环运行的例行性计划任务,linux系统则是由 cron (crond) 这个系统服务来控制的.Linux 系统上面原本就有非常多的计划性工作,因此这个 ...

  8. Caffe+CUDA7.5+CuDNNv3+OpenCV3.0+Ubuntu14.04 配置参考文献 以及 常见编译问题总结

    Caffe+CUDA7.5+CuDNNv3+OpenCV3.0+Ubuntu14.04  配置参考文献 ---- Wang Xiao Warning: Please make sure the cud ...

  9. 修改msde登录方式,设置sa密码为空

    md, 记不得msde怎么修改密码, 每次都要去baidu, 下了个鸟破软件,修改msde密码, 还流氓的安装了360, 写了个批处理,留在这里: net stop MSSQLSERVERreg ad ...

  10. break,continue的使用

    break,continue 使用break命令允许跳出所有循环下面的例子中,脚本进入死循环直至用户输入数字大于5.要跳出这个循环,返回到shell提示符下,就要使用break命令. #!/bin/b ...